Apa perbedaan antara List.of dan Arrays.asList?

117

Java 9 memperkenalkan metode pabrik baru untuk daftar, List.of:

List<String> strings = List.of("first", "second");

Apa perbedaan antara opsi sebelumnya dan baru? Artinya, apa perbedaan antara ini:

Arrays.asList(1, 2, 3);

dan ini:

List.of(1, 2, 3);
ZhekaKozlov
sumber
1
Lihat juga ceramah oleh Stuart "Beaker" Marks.
pengguna1803551
20
@ user1803551 Meskipun saya memahami rasa frustrasi Anda, alasan ini mungkin menjadi preseden yang benar-benar tidak diinginkan. Banyak pertanyaan di sini memiliki jawaban yang 'dinyatakan dengan jelas' (tergantung bagaimana seseorang mendefinisikannya). Saya akan mendorong Anda untuk membawa diskusi ini ke meta tetapi saya cukup yakin diskusi seperti itu seharusnya sudah ada (dan saya berharap seseorang dapat menemukannya dan menautkannya :-)
Dimitris Fasarakis Hilliard
4
@ user1803551 Javadocs tidak menyebutkan perbedaan antara detail implementasi kedua metode ini (seperti konsumsi ruang atau kinerja). Saya pikir orang juga ingin mengetahui detail ini.
ZhekaKozlov
5
@ZhekaKozlov. Jawaban yang diterima dan diberi suara terbanyak juga tidak. Apa yang memberi tahu Anda tentang standar yang diterima? Bahkan memiliki lebih sedikit informasi daripada di dokumen (serialisasi, identitas, pemesanan). Jika ada, ajukan permintaan ke OpenJDK untuk menambahkan info itu.
pengguna1803551
3
Pertanyaan ini sedang dibahas di meta .
Dimitris Fasarakis Hilliard

Jawaban:

172

Arrays.asListmengembalikan daftar yang bisa berubah sementara daftar yang dikembalikan oleh tidakList.of bisa diubah :

List<Integer> list = Arrays.asList(1, 2, null);
list.set(1, 10); // OK

List<Integer> list = List.of(1, 2, 3);
list.set(1, 10); // Fails with UnsupportedOperationException

Arrays.asListmemungkinkan elemen nol sementara List.oftidak:

List<Integer> list = Arrays.asList(1, 2, null); // OK
List<Integer> list = List.of(1, 2, null); // Fails with NullPointerException

contains berperilaku berbeda dengan nulls:

List<Integer> list = Arrays.asList(1, 2, 3);
list.contains(null); // Returns false

List<Integer> list = List.of(1, 2, 3);
list.contains(null); // Fails with NullPointerException

Arrays.asListmengembalikan tampilan dari larik yang dilewati, sehingga perubahan pada larik juga akan tercermin dalam daftar. Karena List.ofini tidak benar:

Integer[] array = {1,2,3};
List<Integer> list = Arrays.asList(array);
array[1] = 10;
System.out.println(list); // Prints [1, 10, 3]

Integer[] array = {1,2,3};
List<Integer> list = List.of(array);
array[1] = 10;
System.out.println(list); // Prints [1, 2, 3]
ZhekaKozlov
sumber
22
Untuk daftar yang berperilaku berbeda berdasarkan bagaimana itu dibangun tampaknya tidak terlalu berorientasi objek bagi saya. Mungkin jika List.of mengembalikan tipe ImmutableList, ini masuk akal. Ini adalah abstraksi yang sangat bocor di sini.
Sandy Chapman
5
Saya bukan pengembang Java, jadi anggap ini sebagai pengamatan biasa. Mungkin ada alasan bagus untuk perilakunya berbeda, tetapi jika saya memiliki metode yang mengembalikan Daftar <Integer> seperti contoh, antarmuka tidak akan cukup bagi saya untuk mengetahui apakah saya akan mendapatkan pengecualian waktu proses jika saya memeriksanya untuk nulls. Demikian pula, perubahan dalam penerapan metode tersebut dapat memengaruhi kode yang jauh dari situs panggilan metode saya jika pemeriksaan itu terjadi di tempat lain. @Nicolai
Sandy Chapman
8
@SandyChapman ini mungkin perilaku yang tidak terduga untuk beberapa (atau sebagian besar?), Tetapi ini adalah perilaku yang didokumentasikan. Dari List.contains(Object o)javadoc : "Throws [...] NullPointerException - jika elemen yang ditentukan adalah null dan daftar ini tidak mengizinkan elemen null (opsional)". Atau dari pengenalan panjang antarmuka yang hanya sedikit dibaca: "Beberapa implementasi koleksi memiliki batasan pada elemen yang mungkin dikandungnya"
Aaron
11
@Aaron baik setidaknya itu abstraksi bocor yang terdokumentasi dengan baik :)
Sandy Chapman
6
@Sandy Chapman: List.of tidak kembali beberapa ImmutableListjenis, nama sebenarnya adalah hanya detail implementasi non-publik. Jika itu publik dan seseorang menayangkannya Listlagi, di mana bedanya? Di manakah perbedaannya Arrays.asList, yang mengembalikan Listimplementasi non-publik , yang memunculkan pengecualian saat mencoba addatau remove, atau daftar yang dikembalikan Collections.unmodifiableListyang tidak memungkinkan modifikasi sama sekali? Ini semua tentang kontrak yang ditentukan di Listantarmuka. Antarmuka Koleksi dengan metode opsional selalu OOP tidak murni sejak Java 1.2…
Holger
31

Perbedaan antara Arrays.asListdanList.of

Lihat JavaDocs dan pembicaraan ini oleh Stuart Marks (atau versi sebelumnya).

Saya akan menggunakan yang berikut ini untuk contoh kode:

List<Integer> listOf = List.of(...);
List<Integer> asList = Arrays.asList(...);
List<Integer> unmodif = Collections.unmodifiableList(asList);

Kekekalan struktural (Atau: tidak dapat dimodifikasi)

Setiap upaya untuk mengubah strukturList.of akan menghasilkan file UnsupportedOperationException. Itu termasuk operasi seperti menambah , mengatur , dan menghapus . Namun, Anda dapat mengubah konten objek dalam daftar (jika objek tersebut tidak dapat diubah), sehingga daftar tersebut tidak "sepenuhnya tidak dapat diubah".

Ini adalah nasib yang sama untuk daftar yang tidak dapat dimodifikasi yang dibuat dengan Collections.unmodifiableList. Hanya daftar ini yang merupakan tampilan dari daftar asli, sehingga dapat berubah jika Anda mengubah daftar aslinya.

Arrays.asListtidak sepenuhnya tidak berubah, tidak ada batasan set.

listOf.set(1, "a");  // UnsupportedOperationException
unmodif.set(1, "a"); // UnsupportedOperationException
asList.set(1, "a");  // modified unmodif! unmodif is not truly unmodifiable

Demikian pula, mengubah larik pendukung (jika Anda menahannya) akan mengubah daftar.

Kekekalan struktural hadir dengan banyak efek samping yang terkait dengan pengkodean pertahanan, konkurensi, dan keamanan yang berada di luar cakupan jawaban ini.

Permusuhan nol

List.ofdan koleksi apa pun sejak Java 1.5 tidak mengizinkannya nullsebagai elemen. Mencoba meneruskan nullsebagai elemen atau bahkan pencarian akan menghasilkan file NullPointerException.

Karena Arrays.asListmerupakan koleksi dari 1.2 (Collections Framework), memungkinkan nulls.

listOf.contains(null);  // NullPointerException
unmodif.contains(null); // allowed
asList.contains(null);  // allowed

Bentuk berseri

Karena List.oftelah diperkenalkan di Java 9 dan daftar yang dibuat dengan metode ini memiliki bentuk serialnya sendiri (biner), daftar tersebut tidak dapat dideserialisasi pada versi JDK sebelumnya (tanpa kompatibilitas biner ). Namun, Anda dapat membatalkan / membuat serial dengan JSON, misalnya.

Identitas

Arrays.asListpanggilan internal new ArrayList, yang menjamin ketidaksetaraan referensi.

List.oftergantung pada implementasi internal. Instans yang dikembalikan dapat memiliki persamaan referensi, tetapi karena ini tidak dijamin, Anda tidak dapat mengandalkannya.

asList1 == asList2; // false
listOf1 == listOf2; // true or false

Perlu disebutkan bahwa daftar adalah sama (melalui List.equals) jika mereka berisi elemen yang sama dalam urutan yang sama, terlepas dari bagaimana mereka dibuat atau operasi apa yang mereka dukung.

asList.equals(listOf); // true i.f.f. same elements in same order

Implementasi (peringatan: detail dapat berubah seiring versi)

Jika jumlah elemen dalam daftar List.of2 atau kurang, elemen disimpan di bidang kelas khusus (internal). Contohnya adalah daftar yang menyimpan 2 elemen (sumber parsial):

static final class List2<E> extends AbstractImmutableList<E> {
    private final E e0;
    private final E e1;

    List2(E e0, E e1) {
        this.e0 = Objects.requireNonNull(e0);
        this.e1 = Objects.requireNonNull(e1);
    }
}

Jika tidak, mereka disimpan dalam larik dengan cara yang mirip dengan Arrays.asList.

Efisiensi Ruang dan Waktu

The List.ofimplementasi yang berbasis lapangan (ukuran <2) melakukan sedikit lebih cepat pada beberapa operasi. Sebagai contoh, size()dapat mengembalikan konstanta tanpa mengambil panjang array, dan contains(E e)tidak memerlukan overhead iterasi.

Membuat daftar yang tidak dapat dimodifikasi melalui List.ofjuga lebih cepat. Bandingkan konstruktor di atas dengan 2 tugas referensi (dan bahkan satu untuk jumlah elemen yang berubah-ubah) ke

Collections.unmodifiableList(Arrays.asList(...));

yang membuat 2 daftar ditambah biaya tambahan lainnya. Dalam hal ruang, Anda menghemat UnmodifiableListpembungkus ditambah beberapa sen. Pada akhirnya, penghematan yang HashSetsetara lebih meyakinkan.


Waktu kesimpulan: gunakan List.ofketika Anda menginginkan daftar yang tidak berubah dan Arrays.asListketika Anda menginginkan daftar yang dapat berubah (seperti yang ditunjukkan di atas).

pengguna1803551
sumber
1
Bagi orang yang bertanya-tanya mengapa jawaban ini ada, lihat ini .
pengguna1803551
3
Arrays.asListtidak sepenuhnya bisa berubah. asList.add(1);melempar UnsupportedOperationException.
mapeters
"Null hostile" adalah cara yang bagus untuk menjelaskannya. Saya hampir tidak dapat menggunakan List.ofwaktu kapan pun orang ingin menelepon containsdan tidak terkejut dengan NullPointerException.
Noumenon
14

Mari kita rangkum perbedaan antara List.of dan Arrays.asList

  1. List.ofdapat digunakan paling baik jika kumpulan data lebih sedikit dan tidak berubah, sementara Arrays.asListdapat digunakan paling baik jika kumpulan data besar dan dinamis.

  2. List.ofmengambil ruang overhead yang sangat sedikit karena memiliki implementasi berbasis lapangan dan mengonsumsi lebih sedikit ruang heap, baik dalam hal overhead tetap dan pada basis per elemen. sementara Arrays.asListmengambil lebih banyak ruang overhead karena saat inisialisasi, ini membuat lebih banyak objek di heap.

  3. Koleksi yang dikembalikan oleh List.oftidak dapat diubah dan karenanya aman untuk thread sementara Koleksi yang dikembalikan oleh Arrays.asListdapat berubah dan tidak aman untuk thread. (Instans pengumpulan yang tidak dapat diubah umumnya mengkonsumsi lebih sedikit memori daripada rekan mereka yang dapat berubah.)

  4. List.oftidak mengizinkan elemen nol sementara Arrays.asListmemungkinkan elemen nol .

Mohit Tyagi
sumber
2
"Contoh pengumpulan yang tidak dapat diubah umumnya mengkonsumsi lebih sedikit memori daripada rekan mereka yang dapat berubah." - Betulkah? Maukah Anda menjelaskan sedikit tentang itu - maksud Anda karena mereka dapat dibagikan dengan aman, atau apakah maksud Anda bahwa contoh itu sendiri dapat diimplementasikan dengan lebih efisien entah bagaimana?
Hulk
1
@ Hulk Penjawabnya benar tentang efisiensi ruang. Lihat pembicaraan Stuart Marks: youtu.be/q6zF3vf114M?t=49m48s
ZhekaKozlov
2
@ZhekaKozlov Itu tampaknya benar secara umum, tetapi saya sangat skeptis bahwa itu benar ketika berbicara tentang Arrays.asListversus List.of, mengingat bahwa yang pertama secara harfiah hanyalah pembungkus di sekitar array. Setidaknya implementasi OpenJDK tampaknya memiliki overhead yang sangat kecil. Faktanya, List.ofperlu membuat salinan dari setiap larik yang diteruskan, jadi kecuali larik itu sendiri akan segera menjadi GC, tampaknya akan List.ofmemiliki jejak memori yang jauh lebih besar.
Chris Hayes
4
@ChrisHayes Setidaknya List.of(x)dan List.of(x, y)lebih efisien karena mereka tidak mengalokasikan array sama sekali
ZhekaKozlov
2
@ Hulk: jangan lupa bahwa List.ofmetode tidak diperlukan untuk mengembalikan daftar baru setiap kali. Daftar ini memiliki identitas yang tidak ditentukan, sehingga mungkin ada cache atau deduplikasi atau skalarisasi yang ditangani pada level JVM. Jika tidak dalam versi ini, mungkin di versi berikutnya. Itu diizinkan oleh kontrak. Sebaliknya, Array.asListbergantung pada identitas larik yang Anda lewati, karena daftar yang dihasilkan adalah tampilan yang bisa berubah pada larik, yang mencerminkan semua perubahan dua arah.
Holger
3

Terlepas dari jawaban di atas ada operasi tertentu yang baik List::ofdan Arrays::asListberbeda:

+----------------------+---------------+----------+----------------+---------------------+
|      Operations      | SINGLETONLIST | LIST::OF | ARRAYS::ASLIST | JAVA.UTIL.ARRAYLIST |
+----------------------+---------------+----------+----------------+---------------------+
|          add         |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        addAll        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         clear        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|        remove        |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       removeAll      |       ❗️       |        |        ❗️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|       retainAll      |       ❗️       |       |        ❗️        |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|      replaceAll      |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|          set         |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|         sort         |       ✔️       |        |        ✔️      |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
|  remove on iterator  |             |       |              |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
| set on list-iterator |             |       |        ✔️       |          ✔️          |
+----------------------+---------------+----------+----------------+---------------------+
  1. ✔️ berarti metode ini didukung
  2. ❌ berarti memanggil metode ini akan memunculkan UnsupportedOperationException
  3. ❗️ berarti metode ini didukung hanya jika argumen metode tidak menyebabkan mutasi, misalnya Collections.singletonList ("foo"). RetretAll ("foo") tidak apa-apa tetapi Collections.singletonList ("foo"). RetretAll ("bar" ) melempar UnsupportedOperationException

Lebih lanjut tentang Koleksi :: singletonList Vs. Daftar

Vishwa Ratna
sumber
1
jawaban untuk ujian java
povis