Koleksi yang Tidak Dapat Diubah Java

115

Dari dokumentasi Java 1.6 Collection Framework :

Koleksi yang tidak mendukung operasi modifikasi apa pun (seperti add, removedan clear) disebut sebagai tidak dapat dimodifikasi . [...] Koleksi yang juga menjamin bahwa tidak ada perubahan pada objek Collection yang akan terlihat disebut sebagai tidak dapat diubah .

Kriteria kedua sedikit membingungkan saya. Mengingat koleksi pertama tidak dapat dimodifikasi, dan dengan asumsi bahwa referensi koleksi asli telah dibuang, apa saja perubahan yang dirujuk pada baris kedua? Apakah ini mengacu pada perubahan elemen yang ada di koleksi, yaitu status elemen?

Pertanyaan kedua:
Agar koleksi tidak dapat diubah, bagaimana cara memberikan jaminan tambahan yang ditentukan? Jika status elemen dalam koleksi diperbarui oleh utas, apakah cukup untuk keabadian bahwa pembaruan dalam keadaan itu tidak terlihat pada utas yang menyimpan koleksi yang tidak bisa diubah?

Agar koleksi tidak dapat diubah, bagaimana cara memberikan jaminan tambahan yang ditentukan?

Bhaskar
sumber
Secara umum (terutama dalam bahasa fungsional), koleksi yang tidak dapat diubah (alias persisten) dapat berubah dalam arti bahwa Anda bisa mendapatkan status baru dari koleksi ini, tetapi pada saat yang sama status lama akan tetap tersedia dari tautan lain. Misalnya, newCol = oldCol.add("element")akan menghasilkan koleksi baru yaitu salinan dari yang lama dengan 1 elemen lagi, dan semua referensi ke oldColakan tetap mengarah ke koleksi lama yang sama dan tidak berubah.
berteman dengan

Jawaban:

154

Koleksi yang tidak dapat dimodifikasi biasanya merupakan tampilan hanya-baca (pembungkus) dari koleksi lain. Anda tidak dapat menambahkan, menghapus, atau menghapusnya, tetapi koleksi yang mendasari dapat berubah.

Koleksi yang tidak dapat diubah tidak dapat diubah sama sekali - mereka tidak membungkus koleksi lain - mereka memiliki elemennya sendiri.

Berikut kutipan dari jambu biji ImmutableList

Tidak seperti Collections.unmodifiableList(java.util.List<? extends T>), yang merupakan tampilan dari kumpulan terpisah yang masih bisa berubah, instance ImmutableListberisi data pribadinya sendiri dan tidak akan pernah berubah.

Jadi, pada dasarnya, untuk mendapatkan koleksi yang tidak bisa diubah dari yang bisa berubah, Anda harus menyalin elemennya ke koleksi baru, dan melarang semua operasi.

Bozho
sumber
@Bhaskar - lihat paragraf terakhir saya.
Bozho
1
jika koleksi yang dibungkus di dalam koleksi lain yang tidak dapat diubah tidak memiliki referensi lain ke dalamnya, apakah gaya koleksi yang tidak dapat diubah itu sama persis dengan koleksi yang tidak dapat diubah?
Bhaskar
1
@Bozho, Jika referensi ke koleksi pendukung tidak disediakan, dan hanya referensi pembungkus koleksi yang tidak dapat dimodifikasi yang disediakan, maka Anda tidak dapat mengubahnya. Lalu mengapa Anda mengatakan "Anda tidak yakin"? Apakah ini menunjukkan skenario di mana beberapa utas atau entah bagaimana itu dimodifikasi karena koleksi pendukung dapat dimodifikasi?
AKS
@AKS: Saat koleksi dibungkus unmodifiableList, kode yang hanya menerima referensi ke daftar itu tidak akan dapat mengubahnya, tetapi kode apa pun yang memiliki referensi ke daftar asli dan dapat mengubahnya sebelum pembungkus dibuat akan tetap dapat melakukannya sesudahnya. Jika kode yang membuat daftar asli mengetahui apa yang terjadi pada setiap referensi yang pernah ada, dan mengetahui bahwa tidak ada yang jatuh ke tangan kode yang mungkin mengubah daftar, maka ia dapat mengetahui bahwa daftar tersebut tidak akan pernah ada. diubah. Namun, jika referensi diterima dari kode luar ...
supercat
... tidak ada cara untuk unmodifiableList, atau kode apa pun yang menggunakannya, untuk mengetahui apakah atau bagaimana koleksi yang dibungkus dapat berubah.
supercat
86

Perbedaannya adalah Anda tidak dapat memiliki referensi ke koleksi yang tidak dapat diubah yang memungkinkan perubahan. Koleksi yang tidak dapat dimodifikasi tidak dapat dimodifikasi melalui referensi itu , tetapi beberapa objek lain mungkin menunjuk ke data yang sama yang dapat diubahnya.

misalnya

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);
Simon Nickerson
sumber
1
berbagai penyebab, jika ada, yang dapat membawa perubahan dalam koleksi yang tidak dapat dimodifikasi, asalkan referensi koleksi asli tidak digunakan untuk mengubah koleksi yang mendasarinya?
Bhaskar
21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1adalah bisa berubah (yaitu tidak unmodifiable atau berubah ).
c2adalah unmodifiable : tidak dapat diubah sendiri, tetapi jika nanti saya mengubah c1kemudian bahwa perubahan akan terlihat dalam c2.

Ini karena c2ini hanyalah pembungkus c1dan bukan salinan independen. Guava menyediakan ImmutableListantarmuka dan beberapa implementasi. Mereka bekerja dengan benar-benar membuat salinan input (kecuali jika input adalah koleksi yang tidak dapat diubah sendiri).

Mengenai pertanyaan kedua Anda:

Mutabilitas / kekekalan sebuah koleksi tidak bergantung pada mutabilitas / kekekalan objek yang terkandung di dalamnya. Memodifikasi objek yang terdapat dalam koleksi tidak dihitung sebagai "modifikasi koleksi" untuk deskripsi ini. Tentu saja jika Anda membutuhkan koleksi yang tidak dapat diubah, Anda biasanya juga menginginkannya berisi objek yang tidak dapat diubah.

Joachim Sauer
sumber
2
@ John: tidak, tidak. Setidaknya tidak sesuai dengan definisi yang dikutip oleh OP.
Joachim Sauer
@JohnVint, benarkah? Menurut saya tidak demikian, karena dengan menggunakan c1, saya masih bisa menambahkan elemen ke dalamnya, dan penambahan ini akan terlihat oleh c2. Bukankah itu berarti itu tidak bisa diubah?
Bhaskar
Saya rasa jika c1 bisa lolos maka itu tidak akan bisa berubah, tapi keabadian juga mengacu pada konten dalam koleksi. Saya lebih mengacu pada Koleksi java.util.Date yang tidak dapat dimodifikasi
John Vint
1
@Vint: "jika c1tidak lolos" bukanlah pertimbangan yang dibedakan di mana pun dalam spesifikasi. Menurut pendapat saya, jika Anda dapat menggunakan koleksi dengan cara yang membuatnya tidak dapat diubah, maka Anda harus selalu menganggapnya tidak dapat diubah.
Joachim Sauer
1
Izinkan saya mengutip definisi: "Koleksi yang juga menjamin ..." (penekanan dari saya). Collection.unmodifiableList() tidak bisa menjamin itu, karena tidak bisa menjamin argumennya tidak lolos. Jambu biji ImmutableList.of selalu menghasilkan yang kekal List, bahkan jika Anda membiarkan argumennya lolos.
Joachim Sauer
17

Sekarang java 9 memiliki Metode pabrik untuk Daftar, Set, Peta, dan Peta yang Tidak Dapat Diubah.

Di Java SE 8 dan versi sebelumnya, Kita dapat menggunakan metode utilitas kelas Koleksi seperti unmodifiableXXX untuk membuat objek Koleksi yang Tidak Dapat Diubah.

Namun metode Collections.unmodifiableXXX ini sangat membosankan dan menggunakan pendekatan verbose. Untuk mengatasi kekurangan tersebut, Oracle corp telah menambahkan beberapa metode utilitas ke antarmuka Daftar, Set, dan Peta.

Sekarang di java 9: ​​- Antarmuka List dan Set memiliki metode "of ()" untuk membuat objek List atau Set yang tidak dapat diubah atau tidak kosong seperti yang ditunjukkan di bawah ini:

Contoh Daftar Kosong

List immutableList = List.of();

Contoh Daftar Tidak Kosong

List immutableList = List.of("one","two","three");
Opster Elasticsearch Pro-Vijay
sumber
2
Dan di Java 10 List.copyOfdan Set.copyOftelah ditambahkan yang memungkinkan membuat salinan daftar / set yang tidak dapat dimodifikasi, atau mengembalikan koleksi yang diberikan jika sudah tidak dapat dimodifikasi, lihat JDK-8191517
Marcono1234
6

Saya percaya intinya di sini adalah bahwa meskipun koleksi tidak dapat dimodifikasi, itu tidak memastikan bahwa itu tidak dapat diubah. Ambil contoh koleksi yang menggusur elemen jika sudah terlalu tua. Tidak dapat dimodifikasi hanya berarti bahwa objek yang memegang referensi tidak dapat mengubahnya, bukan tidak dapat diubah. Contoh nyata dari ini adalah Collections.unmodifiableListmetode. Ini mengembalikan tampilan Daftar yang tidak dapat dimodifikasi. Referensi daftar yang diteruskan ke metode ini masih dapat dimodifikasi sehingga daftar dapat dimodifikasi oleh pemegang referensi yang diteruskan. Hal ini dapat mengakibatkan ConcurrentModificationExceptions dan hal buruk lainnya.

Tidak dapat diubah, artinya koleksi tidak dapat diubah dengan cara apa pun.

Pertanyaan kedua: Koleksi yang tidak dapat diubah tidak berarti bahwa objek yang terdapat dalam koleksi tidak akan berubah, hanya saja koleksi tersebut tidak akan berubah dalam jumlah dan komposisi objek yang dimilikinya. Dengan kata lain, daftar referensi koleksi tidak akan berubah. Itu tidak berarti bahwa bagian dalam dari objek yang direferensikan tidak dapat berubah.

John B
sumber
Bagus !! Jadi jika saya membuat pembungkus di atas Unmodifiable Collection dan menjadikan referensi koleksi yang dapat dimodifikasi sebagai pribadi maka saya dapat yakin bahwa itu tidak dapat diubah. Baik?
AKS
Jika saya mengerti pertanyaan Anda, maka jawabannya tidak. Membungkus Unmodifiable tidak melakukan apa pun untuk melindunginya agar koleksi diteruskan ke unmodifiableListdimodifikasi. Jika Anda ingin melakukan ini gunakan ImmutableList.
John B
0

Pure4J mendukung apa yang Anda kejar, dalam dua cara.

Pertama, ini memberikan @ImmutableValueanotasi, sehingga Anda dapat membuat anotasi kelas untuk mengatakan bahwa itu tidak dapat diubah. Ada plugin maven untuk memungkinkan Anda memeriksa apakah kode Anda benar-benar tidak dapat diubah (penggunaan finaldll.).

Kedua, menyediakan koleksi persisten dari Clojure, (dengan generik tambahan) dan memastikan bahwa elemen yang ditambahkan ke koleksi tidak dapat diubah. Performa tersebut rupanya cukup bagus. Semua koleksi tidak dapat diubah, tetapi menerapkan antarmuka koleksi java (dan generik) untuk diperiksa. Mutasi mengembalikan koleksi baru.

Penafian: Saya pengembang ini

Rob Moffat
sumber