คอลเลกชันที่ไม่เปลี่ยนรูปไม่ได้ vs


170

จากภาพรวม Collections Framework :

คอลเลกชันที่ไม่สนับสนุนการดำเนินการปรับเปลี่ยน (เช่นadd, removeและclear) จะเรียกว่าunmodifiable คอลเลกชันที่ไม่ unmodifiable มีความสามารถปรับเปลี่ยนได้

คอลเลกชันที่ยังรับประกันได้ว่าการเปลี่ยนแปลงในไม่มีCollectionวัตถุที่จะมองเห็นได้จะเรียกว่าไม่เปลี่ยนรูป คอลเลกชันที่ไม่ได้เปลี่ยนรูปเป็นไม่แน่นอน

ฉันไม่เข้าใจความแตกต่าง
ความแตกต่างระหว่างunmodifiableและไม่เปลี่ยนรูปที่นี่คืออะไร

คำตอบ:


207

คอลเลกชันที่ไม่สามารถแก้ไขได้มักจะเป็นตัวหุ้มรอบคอลเลกชันที่แก้ไขได้ซึ่งโค้ดอื่นอาจยังสามารถเข้าถึงได้ ดังนั้นในขณะที่คุณไม่สามารถทำการเปลี่ยนแปลงใด ๆ หากคุณมีการอ้างอิงไปยังคอลเลกชันที่ไม่สามารถเปลี่ยนแปลงได้ แต่คุณไม่สามารถพึ่งพาเนื้อหาที่ไม่เปลี่ยนแปลง

ไม่เปลี่ยนรูปค้ำประกันคอลเลกชันที่ไม่มีอะไรสามารถเปลี่ยนคอลเลกชันใด ๆ เพิ่มเติม ถ้ามันล้อมรอบคอลเลกชันที่แก้ไขได้มันทำให้แน่ใจว่าไม่มีรหัสอื่นสามารถเข้าถึงคอลเลกชันที่แก้ไขได้นั้น โปรดทราบว่าแม้ว่าจะไม่มีรหัสใดสามารถเปลี่ยนแปลงวัตถุที่คอลเลกชันที่มีการอ้างอิงถึงวัตถุเหล่านั้นอาจยังคงไม่แน่นอน - การสร้างคอลเลกชันที่ไม่เปลี่ยนรูปStringBuilderไม่ได้ "หยุด" วัตถุเหล่านั้น

โดยพื้นฐานแล้วความแตกต่างนั้นเกี่ยวกับว่ารหัสอื่นอาจสามารถเปลี่ยนคอลเล็กชันด้านหลังของคุณได้หรือไม่


64
คอลเล็กชันที่ไม่เปลี่ยนรูปไม่รับประกันว่าจะไม่มีอะไรเปลี่ยนแปลงอีกต่อไป มันทำให้แน่ใจว่าตัวสะสมเองไม่สามารถเปลี่ยนแปลงได้ (และไม่ใช่โดยการตัดคำ แต่เป็นการคัดลอก) วัตถุที่มีอยู่ในคอลเลกชันยังคงสามารถเปลี่ยนแปลงได้และไม่มีการรับประกันใด ๆ
สวัสดี Nomus

8
@HieryNomus: โปรดทราบว่าฉันไม่ได้บอกว่าไม่มีอะไรสามารถเปลี่ยนแปลงได้ - ฉันบอกว่าไม่มีสิ่งใดสามารถเปลี่ยนแปลงคอลเลกชันได้
Jon Skeet

1
ตกลงอาจจะเข้าใจผิดนั่น) แต่ก็เป็นการดีที่จะชี้แจงให้ชัดเจน
Hiery Nomus

5
ดังนั้นสิ่งที่คุณพูด สำหรับการเปลี่ยนรูปไม่ได้อย่างแท้จริงคุณต้องมีคอลเลกชันที่ไม่เปลี่ยนรูปซึ่งมีรายการประเภทที่ไม่เปลี่ยนรูป
Evan Plaice

1
@savanibharat: ขึ้นอยู่กับว่ามีโค้ดพา ธ ใดที่สามารถแก้ไขlistได้ หากสิ่งที่ต่อมาสามารถเรียกlist.add(10)แล้วcollจะสะท้อนให้เห็นถึงการเปลี่ยนแปลงที่ดังนั้นไม่ฉันจะไม่เรียกว่าไม่เปลี่ยนรูป
Jon Skeet

92

โดยทั่วไปunModifiableคอลเล็กชันคือมุมมองดังนั้นทางอ้อมอาจยังสามารถ 'แก้ไข' จากการอ้างอิงอื่นที่สามารถแก้ไขได้ นอกจากนี้ยังเป็นเพียงมุมมองแบบอ่านอย่างเดียวของคอลเล็กชั่นอื่น ๆ เมื่อคอลเลกชันต้นฉบับเปลี่ยนคอลเลกชันที่ไม่สามารถเปลี่ยนแปลงได้จะแสดงด้วยค่าล่าสุดเสมอ

อย่างไรก็ตามimmutableคอลเลกชันสามารถถือเป็นสำเนาแบบอ่านอย่างเดียวของคอลเล็กชันอื่นและไม่สามารถแก้ไขได้ ในกรณีนี้เมื่อคอลเลกชันของแหล่งที่มามีการเปลี่ยนแปลงคอลเลกชันที่ไม่เปลี่ยนรูปจะไม่สะท้อนการเปลี่ยนแปลง

นี่คือบททดสอบเพื่อให้เห็นความแตกต่างนี้

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

เอาท์พุต

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--

ฉันไม่สามารถเห็นความแตกต่างใด ๆ คุณช่วยชี้ให้เห็นว่าความแตกต่างไม่เปลี่ยนรูปได้หรือไม่? ฉันเห็นว่าทั้งข้อผิดพลาดที่ไม่เปลี่ยนรูปและไม่สามารถแก้ไขได้กำลังเพิ่มความผิดพลาดและไม่รองรับการเพิ่ม ฉันทำอะไรบางอย่างหายไปหรือเปล่า
AKS

2
@AKS โปรดดูผลลัพธ์ของสามรายการสุดท้ายหลังจากเพิ่ม 'c' ลงในรายการในขณะที่ขนาดmodifiableListและขนาดunModifiableListเพิ่มขึ้นimmutableListไม่เปลี่ยนแปลง
Prashant Bhate

1
Oh! เข้าใจแล้ว! :) .. ดังนั้นที่นี่คุณแก้ไข unmodifableList โดยใช้การเปลี่ยนแปลงใน modifiableList แต่ ImmutableList ไม่สามารถแก้ไขได้ แต่ในทำนองเดียวกันคุณสามารถแก้ไข ImmutableList ได้เช่นกันฉันคิดว่าที่นี่ไคลเอนต์จะได้รับการเข้าถึงการอ้างอิง ImmutableList เท่านั้นอ้างอิงถึง modifiableList ที่ใช้ ImmutableList ที่สร้างขึ้นจะไม่ถูกเปิดเผยกับลูกค้า ขวา?
AKS

1
ใช่เพราะไม่มีการอ้างอิงถึงnew ArrayList<String>(modifiableList)immListList ไม่สามารถแก้ไขได้
Prashant Bhate

@PrashantBhate สวัสดีไม่มีการอ้างอิงถึงnew ArrayList<String>(modifiableList)เพราะnewอะไร? ขอบคุณ
Unheilig

11

ฉันคิดว่าความแตกต่างที่สำคัญคือเจ้าของคอลเลกชันที่ไม่แน่นอนอาจต้องการให้การเข้าถึงคอลเลกชันไปยังรหัสอื่น ๆ แต่ให้การเข้าถึงนั้นผ่านอินเทอร์เฟซที่ไม่อนุญาตให้รหัสอื่นแก้ไขคอลเลกชัน รหัสการเป็นเจ้าของ) ดังนั้นคอลเลกชันจึงไม่เปลี่ยนรูป แต่ผู้ใช้บางคนไม่ได้รับอนุญาตให้เปลี่ยนคอลเลกชัน

บทช่วยสอนเกี่ยวกับJava Collection Wrapperของ Oracle มีสิ่งนี้ให้กล่าว (เน้นเพิ่ม):

wraps ที่ไม่สามารถแก้ไขได้มีสองการใช้งานหลักดังนี้:

  • เพื่อให้คอลเลกชันไม่เปลี่ยนรูปเมื่อมีการสร้างขึ้น ในกรณีนี้เป็นแนวปฏิบัติที่ดีที่จะไม่อ้างอิงการเก็บข้อมูลสำรอง สิ่งนี้รับประกันการไม่เปลี่ยนรูปแบบได้อย่างแน่นอน
  • เพื่อให้ลูกค้าบางรายเข้าถึงโครงสร้างข้อมูลของคุณได้อย่างอ่านอย่างเดียว คุณเก็บการอ้างอิงไปยังคอลเลกชันการสำรองข้อมูล แต่แจกการอ้างอิงไปยัง wrapper ด้วยวิธีนี้ลูกค้าสามารถดู แต่ไม่แก้ไขในขณะที่คุณรักษาเข้าถึงแบบเต็ม

3

ถ้าเราจะพูดคุยเกี่ยวกับ JDK Unmodifiable*เทียบกับฝรั่งImmutable*จริงที่แตกต่างกันนอกจากนี้ยังอยู่ในการปฏิบัติงาน คอลเลกชันที่ไม่เปลี่ยนรูปแบบอาจเป็นได้ทั้งเร็วและมีประสิทธิภาพของหน่วยความจำมากขึ้นหากไม่ได้ล้อมรอบคอลเลกชันปกติ อ้างถึงทีมฝรั่ง :

JDK ให้วิธีการ Collections.unmodifiableXXX แต่ในความเห็นของเราสิ่งเหล่านี้สามารถ

< ... >

  • ไม่มีประสิทธิภาพ: โครงสร้างข้อมูลยังมีค่าใช้จ่ายทั้งหมดของคอลเลกชันที่ไม่แน่นอนรวมถึงการตรวจสอบการปรับเปลี่ยนพร้อมกันพื้นที่พิเศษในตารางแฮช ฯลฯ

เมื่อพิจารณาถึงประสิทธิภาพคุณควรคำนึงถึงว่า wrapper ที่ไม่สามารถแก้ไขได้นั้นไม่ได้คัดลอกคอลเล็กชันซึ่งเป็นรุ่นที่ไม่เปลี่ยนรูปที่ใช้ในฝรั่งและตอนนี้ก็อยู่ใน jdk9 + ด้วยเช่นList.of(...)จะคัดลอกสองครั้งจริง ๆ !
benez

2

ในการอ้างอิงบทสอน Java ™ :

ซึ่งแตกต่างจากการซิงโครไนซ์ห่อหุ้มซึ่งเพิ่มฟังก์ชันการทำงานให้กับคอลเลกชันห่อห่อหุ้ม unmodifiable นำไปใช้งานได้ไป โดยเฉพาะอย่างยิ่งที่พวกเขานำมาใช้ความสามารถในการปรับเปลี่ยนคอลเลกชันโดยการขัดขวางการดำเนินงานทั้งหมดที่จะปรับเปลี่ยนการเก็บรวบรวมและการขว้างปา UnsupportedOperationException wraps ที่ไม่สามารถแก้ไขได้มีสองการใช้งานหลักดังนี้

  • เพื่อให้คอลเลกชันไม่เปลี่ยนรูปเมื่อมีการสร้างขึ้น ในกรณีนี้เป็นแนวปฏิบัติที่ดีที่จะไม่อ้างอิงการเก็บข้อมูลสำรอง สิ่งนี้รับประกันการไม่เปลี่ยนรูปแบบได้อย่างแน่นอน

  • เพื่อให้ลูกค้าบางรายเข้าถึงโครงสร้างข้อมูลของคุณได้อย่างอ่านอย่างเดียว คุณเก็บการอ้างอิงไปยังคอลเลกชันการสำรองข้อมูล แต่แจกการอ้างอิงไปยัง wrapper ด้วยวิธีนี้ลูกค้าสามารถดูได้ แต่ไม่สามารถแก้ไขได้ในขณะที่คุณยังคงเข้าถึงแบบเต็ม

(เน้นที่เหมือง)

สรุปมันขึ้นมาจริงๆ


1

ตามที่ระบุไว้ข้างต้น unmodifiable ไม่เหมือนไม่เปลี่ยนรูปเนื่องจากการเก็บรวบรวม unmodifiable สามารถเปลี่ยนแปลงได้ถ้าตัวอย่างเช่นคอลเลกชัน unmodifiable มีคอลเลกชันของผู้รับมอบสิทธิ์พื้นฐานซึ่งถูกอ้างอิงโดยวัตถุอื่น ๆ และวัตถุนั้นเปลี่ยนแปลง

เกี่ยวกับการไม่เปลี่ยนรูปก็ไม่ได้กำหนดไว้อย่างชัดเจน อย่างไรก็ตามโดยทั่วไปแล้วหมายความว่าวัตถุ "จะไม่เปลี่ยนแปลง" แต่จะต้องมีการกำหนดซ้ำ ตัวอย่างเช่นฉันสามารถนิยามไม่เปลี่ยนรูปในคลาสที่มีตัวแปรอินสแตนซ์ทั้งหมดและวิธีการทั้งหมดไม่มีข้อโต้แย้งและผลตอบแทนพื้นฐาน วิธีการนั้นอนุญาตให้ตัวแปรอินสแตนซ์ซ้ำแล้วซ้ำอีกและวิธีการทั้งหมดจะมีอาร์กิวเมนต์ที่ไม่เปลี่ยนรูปและส่งคืนค่าที่ไม่เปลี่ยนรูป ควรรับประกันวิธีการคืนค่าเดิมตลอดเวลา

สมมติว่าเราสามารถทำสิ่งนั้นได้นอกจากนี้ยังมีเธรดแนวคิดที่ปลอดภัย และคุณอาจถูกชักนำให้เชื่อว่าไม่เปลี่ยนแปลง (หรือเปลี่ยนแปลงไม่ได้เมื่อเวลาผ่านไป) แสดงถึงความปลอดภัยของเธรด อย่างไรก็ตามนั่นไม่ใช่กรณีและนั่นคือประเด็นหลักที่ฉันทำที่นี่ซึ่งยังไม่ได้รับการบันทึกไว้ในคำตอบอื่น ๆ ฉันสามารถสร้างวัตถุที่ไม่เปลี่ยนรูปที่มักจะส่งคืนผลลัพธ์เดียวกัน แต่ก็ไม่ได้เป็นเธรดที่ปลอดภัย หากต้องการดูสิ่งนี้สมมติว่าฉันสร้างคอลเลกชันที่ไม่เปลี่ยนรูปแบบโดยคงการเพิ่มและการลบเมื่อเวลาผ่านไป ตอนนี้คอลเล็กชันที่ไม่เปลี่ยนเปลี่ยนจะส่งคืนองค์ประกอบโดยดูที่คอลเล็กชันภายใน (ซึ่งอาจเปลี่ยนแปลงได้ตลอดเวลา) จากนั้น (ภายใน) การเพิ่มและลบองค์ประกอบที่ถูกเพิ่มหรือลบหลังจากการสร้างคอลเลกชัน เห็นได้ชัดว่าแม้ว่าคอลเลกชันจะส่งคืนองค์ประกอบเดียวกันเสมอ แต่ก็ไม่ได้เป็นเธรดที่ปลอดภัยเพียงเพราะมันจะไม่เปลี่ยนค่า

ตอนนี้เราสามารถนิยามไม่เปลี่ยนรูปเป็นวัตถุที่ปลอดภัยต่อเธรดและจะไม่เปลี่ยนแปลง มีแนวทางสำหรับการสร้างคลาสที่ไม่เปลี่ยนรูปซึ่งโดยทั่วไปจะนำไปสู่คลาสดังกล่าวอย่างไรก็ตามโปรดจำไว้ว่าอาจมีวิธีในการสร้างคลาสที่ไม่เปลี่ยนรูปที่ต้องการความสนใจความปลอดภัยของเธรดเช่นตามที่อธิบายไว้ในตัวอย่างการรวบรวม "snapshot" ด้านบน


1

Java ™ Tutorials พูดดังนี้:

ซึ่งแตกต่างจากการซิงโครไนซ์ห่อหุ้มซึ่งเพิ่มฟังก์ชันการทำงานให้กับคอลเลกชันห่อห่อหุ้ม unmodifiable นำการทำงานออกไป โดยเฉพาะอย่างยิ่งพวกเขาใช้ความสามารถในการปรับเปลี่ยนคอลเลกชันโดยดักการดำเนินงานทั้งหมดที่จะปรับเปลี่ยนคอลเลกชันและโยน UnsupportedOperationException wraps ที่ไม่สามารถแก้ไขได้มีสองการใช้งานหลักดังนี้:

เพื่อให้คอลเลกชันไม่เปลี่ยนรูปเมื่อมีการสร้างขึ้น ในกรณีนี้เป็นแนวปฏิบัติที่ดีที่จะไม่อ้างอิงการเก็บข้อมูลสำรอง สิ่งนี้รับประกันการไม่เปลี่ยนรูปแบบได้อย่างแน่นอน

เพื่อให้ลูกค้าบางรายเข้าถึงโครงสร้างข้อมูลของคุณได้อย่างอ่านอย่างเดียว คุณเก็บการอ้างอิงไปยังคอลเลกชันการสำรองข้อมูล แต่แจกการอ้างอิงไปยัง wrapper ด้วยวิธีนี้ลูกค้าสามารถดูได้ แต่ไม่สามารถแก้ไขได้ในขณะที่คุณยังคงเข้าถึงแบบเต็ม

ฉันคิดว่ามันเป็นคำอธิบายที่ดีพอที่จะเข้าใจความแตกต่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.