คอลเลกชัน Java ไม่เปลี่ยนรูป


115

จากเอกสารJava 1.6 Collection Framework :

คอลเลกชันที่ไม่สนับสนุนการดำเนินการปรับเปลี่ยนใด ๆ (เช่นadd, removeและclear) จะเรียกว่าunmodifiable [ ... ] คอลเลกชันที่ยังรับประกันว่าการเปลี่ยนแปลงในวัตถุเก็บไม่เคยจะมองเห็นได้จะเรียกว่าไม่เปลี่ยนรูป

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

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

เพื่อให้คอลเลกชันไม่เปลี่ยนรูปจะมีวิธีการอย่างไรในการให้การค้ำประกันเพิ่มเติมที่ระบุไว้?


โดยทั่วไป (โดยเฉพาะในภาษาที่ใช้งานได้) คอลเล็กชันที่ไม่เปลี่ยนรูป (หรือที่เรียกว่าถาวร) อาจเปลี่ยนไปในแง่ที่คุณสามารถรับสถานะใหม่ของคอลเล็กชันนี้ได้ แต่ในขณะเดียวกันสถานะเก่าจะยังคงมีอยู่จากลิงก์อื่น ๆ ตัวอย่างเช่นnewCol = oldCol.add("element")จะสร้างคอลเลคชันใหม่ที่เป็นสำเนาเก่าพร้อมองค์ประกอบอีก 1 รายการและการอ้างอิงทั้งหมดของoldColพินัยกรรมยังคงชี้ไปที่คอลเลคชันเก่าที่ไม่มีการเปลี่ยนแปลง
ffriend

คำตอบ:


154

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

ไม่สามารถเปลี่ยนคอลเล็กชันที่ไม่เปลี่ยนรูปได้เลย - ไม่รวมคอลเล็กชันอื่น - มีองค์ประกอบของตัวเอง

นี่คือคำพูดของฝรั่ง ImmutableList

ซึ่งแตกต่างจากCollections.unmodifiableList(java.util.List<? extends T>)มุมมองของคอลเล็กชันแยกต่างหากที่ยังสามารถเปลี่ยนแปลงได้อินสแตนซ์ImmutableListมีข้อมูลส่วนตัวของตัวเองและจะไม่มีวันเปลี่ยนแปลง

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


@Bhaskar - ดูย่อหน้าสุดท้ายของฉัน
Bozho

1
หากคอลเลกชั่นที่ห่อหุ้มไว้ในคอลเลกชั่นอื่นที่ไม่สามารถปรับเปลี่ยนได้ไม่มีการอ้างอิงอื่น ๆ แล้วบาฮาไวร์ของคอลเลกชั่นที่ไม่สามารถปรับเปลี่ยนได้จะเหมือนกับคอลเลกชันที่ไม่เปลี่ยนรูปหรือไม่
Bhaskar

1
@Bozho หากไม่มีการอ้างอิงถึงคอลเลกชันสำรองและมีการระบุเฉพาะข้อมูลอ้างอิงของคอลเลกชันที่ไม่สามารถแก้ไขได้จะไม่มีวิธีใดที่คุณสามารถเปลี่ยนแปลงได้ แล้วทำไมคุณถึงพูดว่า "คุณไม่แน่ใจ"? มันชี้ให้เห็นถึงสถานการณ์ที่เธรดบางส่วนหรือบางส่วนได้รับการแก้ไขเนื่องจากคอลเลกชันสำรองสามารถแก้ไขได้หรือไม่?
AKS

@AKS: เมื่อมีการรวมคอลเล็กชันunmodifiableListโค้ดที่ได้รับเฉพาะการอ้างอิงไปยังรายการนั้นจะไม่สามารถแก้ไขได้ แต่โค้ดใด ๆ ที่มีการอ้างอิงถึงรายการดั้งเดิมและสามารถแก้ไขได้ก่อนที่จะสร้าง wrapper จะยังคงอยู่ สามารถทำได้ในภายหลัง หากรหัสที่สร้างรายการต้นฉบับรู้ว่าเกิดอะไรขึ้นกับทุกการอ้างอิงที่เคยมีมาและรู้ว่าไม่มีรหัสใดที่จะตกอยู่ในมือของรหัสที่อาจแก้ไขรายการได้ก็จะรู้ได้ว่ารายการจะไม่มีวัน การแก้ไข หากได้รับการอ้างอิงจากรหัสภายนอกอย่างไรก็ตาม ...
supercat

... ไม่มีทางสำหรับunmodifiableListหรือรหัสใด ๆ ที่ใช้รหัสนี้จะทราบได้ว่าคอลเลคชันที่รวมไว้อาจเปลี่ยนแปลงไปหรือไม่
supercat

86

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

เช่น

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);

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

21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1คือไม่แน่นอน (เช่นค่าunmodifiableมิได้เปลี่ยนรูป )
c2เป็นunmodifiable : มันไม่สามารถเปลี่ยนแปลงตัวเอง แต่ถ้าภายหลังฉันจะเปลี่ยนc1แล้วว่าc2การเปลี่ยนแปลงจะอยู่ในที่มองเห็นได้

เนื่องจากc2เป็นเพียงกระดาษห่อหุ้มc1และไม่ใช่สำเนาอิสระ ฝรั่งจัดเตรียมImmutableListอินเทอร์เฟซและการใช้งานบางอย่าง ทำงานเหล่านี้โดยการสร้างสำเนาของอินพุตจริง ๆ (เว้นแต่อินพุตนั้นเป็นคอลเล็กชันที่ไม่เปลี่ยนรูปในตัวเอง)

เกี่ยวกับคำถามที่สองของคุณ:

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


2
@ จอห์น: ไม่มันไม่ใช่ อย่างน้อยก็ไม่เป็นไปตามคำจำกัดความที่ OP.
Joachim Sauer

@JohnVint จริงมั้ย? ฉันจะไม่คิดอย่างนั้นเพราะเมื่อใช้ c1 ฉันยังสามารถเพิ่มองค์ประกอบเข้าไปได้และส่วนที่เพิ่มนี้จะมองเห็นได้ใน c2 นั่นไม่ได้หมายความว่ามันไม่เปลี่ยนรูป?
Bhaskar

ฉันเดาว่าถ้า c1 สามารถหลบหนีได้มันก็จะไม่เปลี่ยนรูป แต่ความไม่เปลี่ยนรูปยังหมายถึงเนื้อหาภายในคอลเลกชันด้วย ฉันอ้างถึง unmodifiableCollection ของ java.util.Date's
John Vint

1
@Vint: "ถ้าc1ไม่หนี" ไม่ใช่การพิจารณาที่แตกต่างจากที่ใดในข้อกำหนด ในความคิดของฉันหากคุณสามารถใช้คอลเลคชันในลักษณะที่ทำให้ไม่เปลี่ยนรูปได้คุณควรพิจารณาเสมอว่าไม่เปลี่ยนรูป
Joachim Sauer

1
ขอยกคำจำกัดความ: "คอลเล็กชันที่รับประกันเพิ่มเติม... " (เน้นของฉัน) Collection.unmodifiableList() ไม่สามารถรับประกันได้เพราะไม่สามารถรับประกันได้ว่าการโต้แย้งจะไม่หนีไป ฝรั่งImmutableList.of มักสร้างสิ่งที่ไม่เปลี่ยนรูปเสมอListแม้ว่าคุณจะปล่อยให้ข้อโต้แย้งของมันหลุดรอดไป
Joachim Sauer

17

ตอนนี้java 9มีวิธีการจากโรงงานสำหรับรายการที่ไม่เปลี่ยนรูปชุดแผนที่และแผนที่รายการ

ใน Java SE 8 และเวอร์ชันก่อนหน้าเราสามารถใช้วิธีการยูทิลิตี้คลาส Collections เช่น unmodifiableXXX เพื่อสร้างอ็อบเจ็กต์ Immutable Collection

อย่างไรก็ตามวิธีการ Collections.unmodifiableXXX เหล่านี้เป็นวิธีการที่น่าเบื่อและละเอียดมาก เพื่อเอาชนะข้อบกพร่องเหล่านั้น Oracle corp ได้เพิ่มวิธีการยูทิลิตี้สองสามวิธีในอินเทอร์เฟซ List, Set และ Map

ตอนนี้ใน java 9: ​​- อินเทอร์เฟซ List and Set มีเมธอด“ of ()” ในการสร้าง Immutable List หรือ Set object ที่ว่างเปล่าหรือไม่ว่างเปล่าดังที่แสดงด้านล่าง:

ตัวอย่างรายการที่ว่างเปล่า

List immutableList = List.of();

ตัวอย่างรายการที่ไม่ว่างเปล่า

List immutableList = List.of("one","two","three");

2
และใน Java 10 List.copyOfและSet.copyOfได้รับการเพิ่มซึ่งอนุญาตให้สร้างสำเนารายการ / ชุดที่ไม่สามารถแก้ไขได้หรือส่งคืนคอลเล็กชันที่กำหนดหากยังไม่สามารถแก้ไขได้ให้ดูที่JDK-8191517
Marcono1234

6

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

ไม่เปลี่ยนรูปหมายความว่าไม่สามารถเปลี่ยนแปลงคอลเล็กชันได้

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


สิ่งที่ดี!! ดังนั้นหากฉันสร้างกระดาษห่อหุ้มบน Unmodifiable Collection และทำการอ้างอิงคอลเลกชันที่ปรับเปลี่ยนได้เป็นแบบส่วนตัวฉันก็มั่นใจได้ว่ามันไม่เปลี่ยนรูป ขวา?
AKS

ถ้าฉันเข้าใจคำถามของคุณคำตอบคือไม่ การห่อ Unmodifiable ไม่ได้ทำอะไรเพื่อป้องกันไม่ให้คอลเลกชันถูกส่งต่อไปยังการunmodifiableListแก้ไข หากต้องการใช้งานImmutableListนี้
John B

0

Pure4Jรองรับสิ่งที่คุณเป็นอยู่สองวิธี

ขั้นแรกให้@ImmutableValueคำอธิบายประกอบเพื่อให้คุณสามารถใส่คำอธิบายประกอบชั้นเรียนเพื่อบอกว่าไม่เปลี่ยนรูปได้ มีปลั๊กอิน maven เพื่อให้คุณตรวจสอบว่าโค้ดของคุณไม่เปลี่ยนรูปจริง (ใช้งานfinalฯลฯ )

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

Disclaimer: ฉันเป็นผู้พัฒนาสิ่งนี้

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