Unterschiede zwischen Stream.toList() und Stream.collect(Collectors.toList()) von Java 16?

Lesezeit: 5 Minuten

Knittls Benutzeravatar
stricktl

JDK 16 enthält jetzt a toList() Methode direkt auf Stream Instanzen. In früheren Java-Versionen mussten Sie immer das verwenden collect Methode und stellen Sie eine Collector Beispiel.

Bei der neuen Methode müssen offensichtlich weniger Zeichen eingegeben werden. Sind beide Methoden austauschbar oder gibt es subtile Unterschiede, die man beachten sollte?

var newList = someCollection.stream()
    .map(x -> mapX(x))
    .filter(x -> filterX(x))
    .toList();

// vs.

var oldList = someCollection.stream()
    .map(x -> mapX(x))
    .filter(x -> filterX(x))
    .collect(Collectors.toList());

(Diese Frage ähnelt der Frage „Würde Stream.toList() eine bessere Leistung erbringen als Collectors.toList()“, konzentriert sich jedoch auf das Verhalten und nicht (nur) auf die Leistung.)

Arvind Kumar Avinashs Benutzeravatar
Arvind Kumar Avinash

Ein Unterschied besteht darin Stream.toList() bietet eine List Implementierung, die unveränderlich ist (Typ ImmutableCollections.ListN die nicht hinzugefügt oder sortiert werden können), ähnlich wie bei List.of() und im Gegensatz zum Veränderlichen (veränderbar und sortierbar) ArrayList zur Verfügung gestellt von Stream.collect(Collectors.toList()).

Demo:

import java.util.stream.Stream;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = Stream.of("Hello").toList();
        System.out.println(list);
        list.add("Hi");
    }
}

Ausgang:

[Hello]
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
    at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
    at Main.main(Main.java:8)

überprüfen Sie bitte Dieser Artikel für mehr Details.

Aktualisieren:

Interessant, Stream.toList() gibt a zurück nulls-haltige Liste erfolgreich erstellt.

import java.util.stream.Stream;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Object> list = Stream.of(null, null).toList();
        System.out.println(list);
    }
}

Ausgang:

[null, null]

Andererseits, List.of(null, null) wirft NullPointerException.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Object> list = List.of(null, null);
    }
}

Ausgang:

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:208)
    at java.base/java.util.ImmutableCollections$List12.<init>(ImmutableCollections.java:453)
    at java.base/java.util.List.of(List.java:827)
    at Main.main(Main.java:5)

Notiz: Ich habe verwendet openjdk-16-ea+34_osx-x64 um den Java SE 16-Code zu kompilieren und auszuführen.

Nützliche Ressourcen:

  1. JDK-Fehler#JDK-8180352
  2. Aufruf der Java-Varargs-Methode mit einem einzelnen Nullargument?

  • IIRC, Collectors.toList() Es ist auch nicht garantiert, dass wir eine veränderbare Liste erhalten. Dies geschieht zufälligerweise in den Java-Versionen, die wir bisher gesehen haben.

    – Ole VV

    30. Januar 2021 um 18:14 Uhr

  • @OleV.V. – Richtig. Der in meiner Antwort verlinkte Artikel erwähnt es als: Although there are no guarantees regarding the “type, mutability, serializability, or thread-safety” on the List provided by Collectors.toList(), it is expected that some may have realized it’s currently an ArrayList and have used it in ways that depend on the characteristics of an ArrayList.

    – Arvind Kumar Avinash

    30. Januar 2021 um 18:26 Uhr

  • @Caramiriel Der List Die Schnittstelle wurde absichtlich mit einer Reihe optionaler Methoden entworfen, d. h. Methoden, die implementierende Klassen nicht implementieren müssen und die viele Implementierungen nicht implementieren. Sie sind nicht der Erste, der sich das wundert.

    – Ole VV

    4. Februar 2021 um 20:44

  • Es ist nicht wirklich eine korrekte Aussage, das zu sagen collect(toList()) gibt eine veränderbare Liste zurück; Die Spezifikation ist sehr klar, was es macht Keine Garantien hinsichtlich der Veränderlichkeit der zurückgegebenen Liste. Die aktuelle Implementierung passiert zurückgeben an ArrayList im Augenblick, aber die Spezifikation wurde explizit geschrieben, um eine Änderung zu ermöglichen. Wenn Sie eine veränderbare Liste wünschen, verwenden Sie toCollection(ArrayList::new).

    – Brian Goetz

    19. Februar 2021 um 17:07 Uhr

  • es ist schade, dass List wurde nicht aufgeteilt ReadableList mit get() usw. und WritableList mit set(), add() usw., aber es ist zu spät, es jetzt zu beheben. (Hinweis: Ich bin ursprünglich hierher gekommen, um einen Vorschlag zu machen toCollection(ArrayList::new), was ich immer verwende, wenn ich eine veränderbare Liste benötige, aber der Mann selbst ist mir zuvorgekommen. 😂)

    – Clement Cherlin

    22. Februar 2021 um 14:05 Uhr


ZhekaKozlovs Benutzeravatar
ZhekaKozlov

Hier ist eine kleine Tabelle, die die Unterschiede zwischen ihnen zusammenfasst Stream.collect(Collectors.toList()), Stream.collect(Collectors.toUnmodifiableList()) Und Stream.toList():

Methode Garantiert Unveränderbarkeit Erlaubt Nullen
collect(toList()) NEIN Ja
collect(toUnmodifiableList()) Ja NEIN
toList() Ja Ja

Noch ein kleiner Unterschied:

// Compiles
List<CharSequence> list = Stream.of("hello", "world").collect(toList());

// Error
List<CharSequence> list = Stream.of("hello", "world").toList();

  • Die neue Methode Stream.toList() erzeugt weder eine nicht veränderbare Liste noch eine Verknüpfung zum Sammeln (toUnmodifiableList()), da toUnmodifiableList() keine Nullen akzeptiert. Die Implementierung von Stream.toList() wird nicht durch die Collector-Schnittstelle eingeschränkt; Daher reserviert Stream.toList() weniger Speicher. Daher eignet es sich optimal, wenn die Streamgröße im Voraus bekannt ist. blogs.oracle.com/javamagazine/hidden-gems-jdk16-jdk17-jep Können Sie die obigen Aussagen bestätigen?

    – deepakl.2000

    28. Juli 2021 um 18:19 Uhr


  • Ich möchte hinzufügen, dass der erste in Java 8 eingeführt wurde, der zweite in Java 10 und der dritte in Java 16.

    – Guillaume Husta

    5. Dezember 2022 um 10:05 Uhr

.collect(toList()) Und toList() benehmen unterschiedlich hinsichtlich der Subtypkompatibilität der Elemente in den erstellten Listen.

Schauen Sie sich die folgenden Alternativen an:

  • List<Number> old = Stream.of(0).collect(Collectors.toList()); funktioniert gut, obwohl wir einen Stream sammeln Ganze Zahl in eine Liste von Nummer.
  • List<Number> new = Stream.of(0).toList(); ist die entsprechende Java 16+-Version, wird jedoch nicht kompiliert (cannot convert from List<Integer> to List<Number>; zumindest in ecj, dem Eclipse-Java-Compiler).

Es gibt mindestens zwei Problemumgehungen, um den Kompilierungsfehler zu beheben:

  • Explizit in den gewünschten Typ umgewandelt
    List<Number> fix1 = Stream.of(0).map(Number.class::cast).toList();
  • Untertypen in der Ergebnissammlung zulassen:
    List<? extends Number> fix2 = Stream.of(0).toList();

Nach meinem Verständnis ist die Grundursache folgende: Der generische Typ T von Java 16 toList() ist derselbe wie der generische Typ T des Streams selbst. Der generische Typ T von Collectors.toList() wird jedoch von der linken Seite der Zuweisung weitergegeben. Wenn diese beiden Typen unterschiedlich sind, werden beim Ersetzen aller alten Anrufe möglicherweise Fehler angezeigt.

1453250cookie-checkUnterschiede zwischen Stream.toList() und Stream.collect(Collectors.toList()) von Java 16?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy