Was ist der Unterschied zwischen List.of und Arrays.asList?

117

Java 9 führte neue Factory-Methoden für Listen ein List.of:

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

Was ist der Unterschied zwischen der vorherigen und der neuen Option? Das heißt, was ist der Unterschied zwischen diesem:

Arrays.asList(1, 2, 3);

und das:

List.of(1, 2, 3);
ZhekaKozlov
quelle
1
Siehe auch diesen Vortrag von Stuart "Beaker" Marks.
user1803551
20
@ user1803551 Obwohl ich Ihre Frustration verstehe, könnte diese Argumentation einen wirklich unerwünschten Präzedenzfall darstellen. Viele Fragen hier haben eine Antwort, die "klar formuliert" ist (je nachdem, wie man dies definiert). Ich möchte Sie dringend bitten, diese Diskussion auf Meta zu bringen, aber ich bin mir ziemlich sicher, dass eine solche Diskussion bereits existieren sollte (und ich hoffe, dass jemand sie finden und verknüpfen kann :-)
Dimitris Fasarakis Hilliard
4
@ user1803551 Javadocs erwähnen den Unterschied zwischen den Implementierungsdetails dieser beiden Methoden (wie Speicherplatzverbrauch oder Leistung) nicht. Ich denke, die Leute würden diese Details auch gerne wissen.
ZhekaKozlov
5
@ZhekaKozlov Die akzeptierte und überbewertete Antwort auch nicht. Was sagt Ihnen das über die akzeptierten Standards? Es enthält sogar weniger Informationen als in den Dokumenten (Serialisierung, Identität, Bestellung). Wenn überhaupt, senden Sie eine Anfrage an OpenJDK, um diese Informationen hinzuzufügen.
user1803551
3
Diese Frage wird auf Meta diskutiert .
Dimitris Fasarakis Hilliard

Antworten:

173

Arrays.asListliefert eine veränderbare Liste , während die Liste zurück durch List.ofist unveränderlich :

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.asListerlaubt null Elemente, während List.ofnicht:

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

contains verhält sich anders mit Nullen:

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.asListGibt eine Ansicht des übergebenen Arrays zurück, sodass die Änderungen am Array auch in der Liste angezeigt werden. Denn List.ofdas ist nicht wahr:

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
quelle
22
Es scheint mir nicht sehr objektorientiert zu sein, wenn sich eine Liste je nach Aufbau anders verhält. Wenn List.of einen ImmutableList-Typ zurückgibt, ist dies möglicherweise sinnvoll. Dies ist hier eine sehr undichte Abstraktion.
Sandy Chapman
5
Ich bin kein Java-Entwickler, nehmen Sie es also als beiläufige Beobachtung. Es gibt möglicherweise einen guten Grund für ein unterschiedliches Verhalten, aber wenn ich eine Methode hätte, die eine List <Integer> wie im Beispiel zurückgibt, würde die Schnittstelle nicht ausreichen, um zu wissen, ob ich eine Laufzeitausnahme erhalte, wenn ich sie überprüfe für Nullen. Ebenso kann sich eine Änderung der Implementierung dieser Methode auf Code auswirken, der von der Aufrufsite meiner Methode entfernt ist, wenn diese Überprüfung an anderer Stelle erfolgt. @Nicolai
Sandy Chapman
8
@ SandyChapman Dies kann für einige (oder die meisten?) Ein unerwartetes Verhalten sein, aber es ist ein dokumentiertes Verhalten. Aus dem List.contains(Object o)Javadoc des : "Löst eine [...] NullPointerException aus - wenn das angegebene Element null ist und diese Liste keine Nullelemente zulässt (optional)". Oder aus der langen Einführung der Benutzeroberfläche, die nur wenige lesen: "Einige Sammlungsimplementierungen haben Einschränkungen hinsichtlich der Elemente, die sie enthalten können"
Aaron,
11
@ Aaron gut, zumindest ist es eine gut dokumentierte undichte Abstraktion :)
Sandy Chapman
6
@Sandy Chapman: List.of nicht etwas zurückgeben ImmutableListTyp, dessen eigentlicher Name ist nur eine nicht-öffentliche Implementierung Detail. Wenn es öffentlich war und jemand es Listerneut besetzte, wo war der Unterschied? Wo ist der Unterschied zu Arrays.asList, der eine nicht öffentliche ListImplementierung zurückgibt, die beim Versuch addoder eine Ausnahme auslöst remove, oder zu der Liste, von Collections.unmodifiableListder zurückgegeben wird , die überhaupt keine Änderung zulässt? Es geht um Verträge, die in der ListBenutzeroberfläche angegeben sind. Die Collections-Schnittstellen mit optionalen Methoden waren seit Java 1.2…
Holger
31

Die Unterschiede zwischen Arrays.asListundList.of

Siehe die JavaDocs und diesen Vortrag von Stuart Marks (oder früheren Versionen davon).

Für die Codebeispiele verwende ich Folgendes:

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

Strukturelle Unveränderlichkeit (oder: Unmodifizierbarkeit)

Jeder Versuch, sich strukturell zu verändern, List.offührt zu einem UnsupportedOperationException. Dazu gehören Vorgänge wie Hinzufügen , Festlegen und Entfernen . Sie können jedoch den Inhalt der Objekte in der Liste ändern (wenn die Objekte nicht unveränderlich sind), sodass die Liste nicht "vollständig unveränderlich" ist.

Dies ist das gleiche Schicksal für nicht veränderbare Listen, die mit erstellt wurden Collections.unmodifiableList. Nur diese Liste ist eine Ansicht der ursprünglichen Liste, sodass sie sich ändern kann, wenn Sie die ursprüngliche Liste ändern.

Arrays.asListist nicht vollständig unveränderlich, es gibt keine Einschränkung auf set.

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

In ähnlicher Weise ändert das Ändern des Hintergrundarrays (wenn Sie es gedrückt halten) die Liste.

Strukturelle Unveränderlichkeit bringt viele Nebenwirkungen mit sich, die mit defensiver Codierung, Parallelität und Sicherheit zusammenhängen und über den Rahmen dieser Antwort hinausgehen.

Null Feindseligkeit

List.ofund jede Sammlung seit Java 1.5 nicht nullals Element zulassen . Der Versuch, nullals Element oder sogar als Suche zu übergeben, führt zu a NullPointerException.

Da Arrays.asListes sich um eine Sammlung aus 1.2 (dem Collections Framework) handelt, können nulls.

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

Serialisierte Form

Da List.ofes in Java 9 eingeführt wurde und die mit dieser Methode erstellten Listen eine eigene (binäre) serialisierte Form haben, können sie in früheren JDK-Versionen nicht deserialisiert werden (keine Binärkompatibilität ). Sie können jedoch beispielsweise mit JSON de / serialisieren.

Identität

Arrays.asListInterne Anrufe new ArrayList, die Referenzungleichheit garantieren.

List.ofhängt von der internen Implementierung ab. Die zurückgegebenen Instanzen können Referenzgleichheit haben, aber da dies nicht garantiert ist, können Sie sich nicht darauf verlassen.

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

Erwähnenswert ist, dass Listen gleich sind (via List.equals), wenn sie dieselben Elemente in derselben Reihenfolge enthalten, unabhängig davon, wie sie erstellt wurden oder welche Operationen sie unterstützen.

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

Implementierung (Warnung: Details können sich über Versionen ändern)

Wenn die Anzahl der Elemente in der Liste von List.of2 oder weniger beträgt, werden die Elemente in Feldern einer speziellen (internen) Klasse gespeichert. Ein Beispiel ist die Liste, in der 2 Elemente gespeichert sind (Teilquelle):

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);
    }
}

Andernfalls werden sie auf ähnliche Weise wie in einem Array gespeichert Arrays.asList.

Zeit- und Raumeffizienz

Die List.offeldbasierten Implementierungen (Größe <2) arbeiten bei einigen Operationen etwas schneller. Als Beispiele size()können eine konstante zurückzukehren , ohne die Arraylänge Abrufen und contains(E e)nicht Iteration Overhead erfordert.

Das Erstellen einer nicht veränderbaren Liste über List.ofist ebenfalls schneller. Vergleichen Sie den obigen Konstruktor mit 2 Referenzzuweisungen (und sogar der für eine beliebige Anzahl von Elementen) mit

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

Dadurch werden 2 Listen plus anderer Overhead erstellt. In Bezug auf den Platz sparen Sie den UnmodifiableListWrapper plus ein paar Cent. Letztendlich sind die Einsparungen im HashSetÄquivalent überzeugender.


Abschlusszeit: Verwenden List.ofSie diese Option, wenn Sie eine Liste wünschen, die sich nicht ändert, undArrays.asList wenn Sie eine Liste möchten, die sich ändern kann (wie oben gezeigt).

user1803551
quelle
1
Wenn Sie sich fragen, warum diese Antwort existiert, lesen Sie diese .
user1803551
3
Arrays.asListist nicht vollständig veränderlich. asList.add(1);wirft eine UnsupportedOperationException.
Mapeters
"Null feindlich" ist eine großartige Möglichkeit, es auszudrücken. Ich kann so gut wie List.ofkeine Zeit nutzen, die Leute anrufen möchten, containsund mich nicht von einer NullPointerException überraschen lassen.
Noumenon
14

Fassen wir die Unterschiede zwischen List.of und Arrays.asList zusammen

  1. List.ofkann am besten verwendet werden, wenn der Datensatz kleiner und unverändert ist, während Arrays.asListam besten bei großen und dynamischen Datensätzen verwendet werden kann.

  2. List.ofNehmen Sie sehr wenig Overhead-Speicherplatz in Anspruch, da die Implementierung vor Ort erfolgt und weniger Heap-Speicherplatz verbraucht wird, sowohl in Bezug auf den festen Overhead als auch auf Elementbasis. während Arrays.asListnehmen mehr Kopfraum , weil während der Initialisierung es mehr Objekte im Heap erzeugt.

  3. Die von zurückgegebene Sammlung List.ofist unveränderlich und daher threadsicher, während die von zurückgegebene Sammlung Arrays.asListveränderlich und nicht threadsicher ist. (Unveränderliche Sammlungsinstanzen verbrauchen im Allgemeinen viel weniger Speicher als ihre veränderlichen Gegenstücke.)

  4. List.oferlaubt keine Nullelemente , während NullelementeArrays.asList erlaubt sind.

Mohit Tyagi
quelle
2
"Unveränderliche Sammlungsinstanzen verbrauchen im Allgemeinen viel weniger Speicher als ihre veränderlichen Gegenstücke." - Ja wirklich? Möchten Sie etwas näher darauf eingehen - meinen Sie, weil sie sicher geteilt werden können, oder meinen Sie, dass die Instanzen selbst irgendwie effizienter implementiert werden können?
Hulk
1
@Hulk Der Antwortende hat Recht mit der Raumeffizienz. Siehe Stuart Marks 'Vortrag: youtu.be/q6zF3vf114M?t=49m48s
ZhekaKozlov
2
@ ZhekaKozlov Das scheint im Allgemeinen wahr zu sein, aber ich bin sehr skeptisch, dass es wahr ist, wenn ich darüber spreche Arrays.asList Versus geht List.of, da Ersteres buchstäblich nur ein Wrapper um ein Array ist. Zumindest die Implementierung von OpenJDK scheint einen extrem geringen Overhead zu haben. Tatsächlich List.ofmüssten Kopien aller übergebenen Arrays erstellt werden. Wenn das Array selbst nicht bald einer GC unterzogen wird, scheint List.ofes einen erheblich größeren Speicherbedarf zu haben.
Chris Hayes
4
@ ChrisHayes Zumindest List.of(x)und List.of(x, y)sind effizienter, weil sie überhaupt keine Arrays zuweisen
ZhekaKozlov
2
@Hulk: Vergessen Sie nicht, dass die List.ofMethoden nicht jedes Mal neue Listen zurückgeben müssen. Diese Listen haben eine nicht spezifizierte Identität, sodass auf JVM-Ebene möglicherweise zwischengespeichert, dedupliziert oder skaliert wird. Wenn nicht in dieser Version, dann vielleicht in der nächsten. Es ist vertraglich erlaubt. Im Gegensatz dazu Array.asListhängt dies von der Identität des Arrays ab, das Sie übergeben, da die resultierende Liste eine veränderbare Ansicht des Arrays ist, die alle Änderungen bidirektional widerspiegelt.
Holger
2

Abgesehen von den oben genannten Antworten gibt es bestimmte Operationen an den beide List::ofund Arrays::asListunterscheiden:

+----------------------+---------------+----------+----------------+---------------------+
|      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. ✔️ bedeutet, dass die Methode unterstützt wird
  2. ❌ bedeutet, dass beim Aufrufen dieser Methode eine UnsupportedOperationException ausgelöst wird
  3. ❗️ bedeutet, dass die Methode nur unterstützt wird, wenn die Argumente der Methode keine Mutation verursachen, z. B. Collections.singletonList ("foo"). RetainAll ("foo") ist in Ordnung, Collections.singletonList ("foo"). RetainAll ("bar") ) löst eine UnsupportedOperationException aus

Mehr über Informationen zu Collections :: singletonList Vs. Liste von

Vishwa Ratna
quelle
1
Antwort für Java-Prüfung
Povis