Koleksi Immutable vs Unmodifiable

170

Dari Ikhtisar Kerangka Kerja Koleksi :

Koleksi yang tidak mendukung operasi modifikasi (seperti add, removedan clear) disebut sebagai tidak dapat dimodifikasi . Koleksi yang tidak dapat dimodifikasi dapat dimodifikasi .

Koleksi yang juga menjamin bahwa tidak ada perubahan pada Collectionobjek akan terlihat disebut tidak berubah . Koleksi yang tidak bisa diubah tidak bisa berubah .

Saya tidak bisa memahami perbedaannya.
Apa perbedaan antara tidak dapat dimodifikasi dan tidak berubah di sini?

Cratylus
sumber

Jawaban:

207

Koleksi yang tidak dapat dimodifikasi sering kali merupakan pembungkus di sekitar koleksi yang dapat dimodifikasi yang masih dapat diakses oleh kode lain . Jadi, sementara Anda tidak dapat membuat perubahan apa pun jika Anda hanya memiliki referensi ke koleksi yang tidak dapat dimodifikasi, Anda tidak bisa mengandalkan konten yang tidak berubah.

Sebuah abadi jaminan koleksi yang ada dapat mengubah koleksi lagi. Jika membungkus koleksi yang dapat dimodifikasi, pastikan tidak ada kode lain yang memiliki akses ke koleksi yang dapat dimodifikasi tersebut. Perhatikan bahwa meskipun tidak ada kode yang dapat mengubah objek yang berisi koleksi referensi, objek itu sendiri mungkin masih bisa berubah - membuat koleksi abadi StringBuildertidak entah bagaimana "membekukan" objek tersebut.

Pada dasarnya, perbedaannya adalah tentang apakah kode lain mungkin dapat mengubah koleksi di belakang Anda.

Jon Skeet
sumber
63
Koleksi abadi tidak menjamin bahwa tidak ada yang bisa berubah lagi. Itu hanya memastikan bahwa koleksi itu sendiri tidak dapat diubah (dan bukan dengan membungkus, tetapi dengan menyalin). Benda-benda yang ada dalam koleksi masih bisa diubah, dan tidak ada jaminan diberikan pada mereka.
Hiery Nomus
8
@HieryNomus: Perhatikan bahwa saya tidak mengatakan bahwa tidak ada yang bisa berubah - saya katakan tidak ada yang bisa mengubah koleksi.
Jon Skeet
1
ok, mungkin sudah salah baca;) Tapi bagus untuk memperjelasnya.
Hiery Nomus
5
Jadi, apa yang kamu katakan. Adalah untuk kekekalan sejati Anda memerlukan koleksi kekal yang berisi item dari tipe kekal.
Evan Plaice
1
@savanibharat: itu tergantung pada apakah ada jalur kode yang masih bisa dimodifikasi list. Jika sesuatu nanti bisa memanggil list.add(10)maka collakan mencerminkan perubahan itu, jadi tidak, saya tidak akan menyebutnya tidak berubah.
Jon Skeet
92

Pada dasarnya unModifiableKoleksi adalah pandangan, Jadi secara tidak langsung masih bisa 'dimodifikasi' dari beberapa referensi lain yang dapat dimodifikasi. Juga sebagai tampilan hanya baca koleksi annother, Ketika koleksi sumber berubah Koleksi tidak dapat diverifikasi akan selalu hadir dengan nilai-nilai terbaru.

Namun immutableKoleksi dapat diperlakukan sebagai salinan hanya baca dari koleksi lain dan tidak dapat dimodifikasi. Dalam hal ini ketika kumpulan sumber berubah, Koleksi tidak berubah tidak mencerminkan perubahan

Berikut adalah testcase untuk memvisualisasikan perbedaan ini.

@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());

}

Keluaran

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]
--
Prashant Bhate
sumber
Saya tidak dapat melihat perbedaan, bisakah Anda menunjukkan betapa Immutable berbeda? Saya dapat melihat Immutable dan unmodifiable melemparkan kesalahan dan menambahkan tidak didukung. Apakah saya melewatkan sesuatu di sini?
AKS
2
@AKS Silakan lihat output dari tiga entri daftar terakhir setelah penambahan 'c' ke daftar sementara kedua ukuran modifiableListdan unModifiableListpeningkatan immutableListukuran tidak berubah
Prashant Bhate
1
Oh! mengerti! :) .. Jadi di sini Anda memodifikasi unmodifableList menggunakan perubahan dalam modifiableList, tetapi ImmutableList tidak dapat dimodifikasi. Tetapi dengan cara yang sama Anda dapat memodifikasi ImmutableList juga, saya pikir di sini klien akan mendapatkan akses ke referensi ImmutableList saja, referensi ke modifiableList, menggunakan ImmutableList yang dibuat, tidak akan terpapar ke klien. Baik?
AKS
1
ya karena tidak ada referensi ke new ArrayList<String>(modifiableList)immutableList tidak dapat dimodifikasi
Prashant Bhate
@ PrashantBhate Hai, tidak ada referensi new ArrayList<String>(modifiableList)karena new? Terima kasih.
Unheilig
11

Saya pikir perbedaan utamanya adalah bahwa pemilik koleksi yang dapat diubah mungkin ingin memberikan akses ke koleksi ke beberapa kode lain, tetapi menyediakan akses itu melalui antarmuka yang tidak memperbolehkan kode lain untuk memodifikasi koleksi (sambil menyimpan kemampuan itu ke kode yang dimiliki). Jadi koleksi tidak dapat diubah, tetapi pengguna tertentu tidak diizinkan untuk mengubah koleksi.

Tutorial Java Collection Wrapper dari Oracle mengatakan hal ini (penekanan ditambahkan):

Pembungkus yang tidak dapat dimodifikasi memiliki dua kegunaan utama, sebagai berikut:

  • Untuk membuat koleksi tidak berubah setelah dibangun. Dalam hal ini, praktik yang baik untuk tidak mempertahankan referensi ke koleksi dukungan. Ini benar-benar menjamin kekekalan.
  • Untuk memungkinkan klien tertentu akses hanya baca ke struktur data Anda. Anda menyimpan referensi ke koleksi dukungan tetapi membagikan referensi ke pembungkus. Dengan cara ini, klien dapat melihat tetapi tidak memodifikasi, sementara Anda mempertahankan akses penuh .
Michael Burr
sumber
3

Jika kita berbicara tentang JDK Unmodifiable*vs jambu Immutable*, sebenarnya perbedaannya juga dalam kinerja . Koleksi yang tidak dapat diubah dapat menjadi lebih cepat dan lebih hemat-memori jika mereka bukan pembungkus koleksi biasa (implementasi JDK adalah pembungkus). Mengutip tim jambu :

JDK menyediakan metode Collections.unmodifiableXXX, tetapi menurut kami, ini bisa

<...>

  • tidak efisien: struktur data masih memiliki semua overhead koleksi bisa berubah, termasuk pemeriksaan modifikasi bersamaan, ruang ekstra dalam tabel hash, dll.
Dmide
sumber
memikirkan kinerja, Anda juga harus mempertimbangkan bahwa pembungkus yang tidak dapat dimodifikasi tidak menyalin koleksi, sedangkan versi yang tidak dapat diubah digunakan dalam jambu biji dan sekarang juga di jdk9 + dengan misalnya misalnya List.of(...)memang menyalin dua kali!
benez
2

Mengutip Tutorial Java ™ :

Tidak seperti pembungkus sinkronisasi, yang menambah fungsionalitas ke koleksi yang dibungkus, pembungkus yang tidak dapat dimodifikasi menghilangkan fungsi. Secara khusus, mereka menghilangkan kemampuan untuk memodifikasi koleksi dengan mencegat semua operasi yang akan mengubah koleksi dan melempar UnsupportedOperationException . Pembungkus yang tidak dapat dimodifikasi memiliki dua kegunaan utama, sebagai berikut:

  • Untuk membuat koleksi tidak berubah setelah dibangun. Dalam hal ini, praktik yang baik untuk tidak mempertahankan referensi ke koleksi dukungan. Ini benar-benar menjamin kekekalan.

  • Untuk memungkinkan klien tertentu akses hanya baca ke struktur data Anda. Anda menyimpan referensi ke koleksi dukungan tetapi membagikan referensi ke pembungkus. Dengan cara ini, klien dapat melihat tetapi tidak memodifikasi, sementara Anda mempertahankan akses penuh.

(penekanan milikku)

Ini benar-benar merangkumnya.

Bharath
sumber
1

Seperti disebutkan di atas yang tidak dapat dimodifikasi tidak seperti yang tidak dapat diubah karena koleksi yang tidak dapat dimodifikasi dapat diubah jika misalnya koleksi yang tidak dapat dimodifikasi memiliki koleksi delegasi yang mendasarinya yang dirujuk oleh beberapa objek lain dan objek mengubahnya.

Mengenai abadi, itu bahkan tidak didefinisikan dengan baik. Namun, secara umum itu berarti bahwa objek "tidak akan berubah", tetapi itu perlu didefinisikan secara rekursif. Sebagai contoh, saya dapat mendefinisikan tidak dapat diubah pada kelas yang variabel instan semuanya primitif dan yang metodenya tidak mengandung argumen dan mengembalikan primitif. Metode-metode kemudian secara rekursif memungkinkan variabel instan menjadi tidak dapat diubah dan semua metode mengandung argumen yang tidak dapat diubah dan yang mengembalikan nilai yang tidak dapat diubah. Metode harus dijamin untuk mengembalikan nilai yang sama dari waktu ke waktu.

Dengan asumsi kita bisa melakukan itu, ada juga konsep thread safe. Dan Anda mungkin dituntun untuk percaya bahwa kekekalan (atau tidak berubah seiring waktu) juga menyiratkan keamanan thread. Namun bukan itu masalahnyadan itulah poin utama yang saya sampaikan di sini yang belum dicatat dalam jawaban lain. Saya dapat membangun objek yang tidak berubah yang selalu mengembalikan hasil yang sama namun tidak aman untuk thread. Untuk melihat ini anggaplah bahwa saya membangun koleksi abadi dengan mempertahankan penambahan dan penghapusan dari waktu ke waktu. Sekarang koleksi abadi mengembalikan elemen-elemennya dengan melihat koleksi internal (yang mungkin berubah dari waktu ke waktu) dan kemudian (secara internal) menambah dan menghapus elemen-elemen yang ditambahkan atau dihapus setelah pembuatan koleksi. Jelas, meskipun koleksi akan selalu mengembalikan elemen yang sama, itu bukan thread aman semata karena tidak akan pernah mengubah nilai.

Sekarang kita dapat mendefinisikan immutable sebagai objek yang aman untuk thread dan tidak akan pernah berubah. Ada panduan untuk membuat kelas yang tidak dapat diubah yang umumnya mengarah ke kelas tersebut, namun perlu diingat bahwa mungkin ada cara untuk membuat kelas yang tidak dapat diubah, yang memerlukan perhatian pada keamanan utas, misalnya, seperti dijelaskan dalam contoh koleksi "snapshot" di atas.

dan b
sumber
1

Tutorial Java ™ mengatakan yang berikut:

Tidak seperti pembungkus sinkronisasi, yang menambah fungsionalitas ke koleksi yang dibungkus, pembungkus yang tidak dapat dimodifikasi menghilangkan fungsi. Secara khusus, mereka menghilangkan kemampuan untuk memodifikasi koleksi dengan mencegat semua operasi yang akan memodifikasi koleksi dan melempar UnsupportedOperationException. Pembungkus yang tidak dapat dimodifikasi memiliki dua kegunaan utama, sebagai berikut:

Untuk membuat koleksi tidak berubah setelah dibangun. Dalam hal ini, praktik yang baik untuk tidak mempertahankan referensi ke koleksi dukungan. Ini benar-benar menjamin kekekalan.

Untuk memungkinkan klien tertentu akses hanya baca ke struktur data Anda. Anda menyimpan referensi ke koleksi dukungan tetapi membagikan referensi ke pembungkus. Dengan cara ini, klien dapat melihat tetapi tidak memodifikasi, sementara Anda mempertahankan akses penuh.

Saya pikir ini penjelasan yang cukup baik untuk memahami perbedaannya.

piyush121
sumber