Различия между Java 16 Stream.toList() и Stream.collect(Collectors.toList())?

JDK 16 теперь включает toList() метод непосредственно в Stream экземплярах. В предыдущих версиях Java всегда приходилось использовать метод collect и предоставлять экземпляр Collector.

Новый метод, очевидно, требует ввода меньшего количества символов. Являются ли оба метода взаимозаменяемыми или есть тонкие различия, о которых следует знать?

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

(Этот вопрос аналогичен Будет ли Stream.toList() работать лучше, чем Collectors .toList(), но сосредоточены на поведении, а не (только) на производительности.)


person knittl    schedule 30.01.2021    source источник


Ответы (2)


Одно из отличий состоит в том, что Stream.toList() предоставляет неизменяемую реализацию List (тип ImmutableCollections.ListN, который нельзя добавлять или сортировать), аналогичную той, что предоставляется List.of(), и в отличие от изменяемой (можно изменять и сортировать) ArrayList, предоставляемой Stream.collect(Collectors.toList()).

Демонстрация:

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

Вывод:

[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)

Пожалуйста, проверьте эту статью для более подробной информации.

Обновлять:

Интересно, что Stream.toList() успешно возвращает список, содержащий nulls.

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

Вывод:

[null, null]

С другой стороны, List.of(null, null) бросает NullPointerException.

import java.util.List;

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

Вывод:

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)

Примечание. Я использовал openjdk-16-ea+34_osx-x64 для компиляции и выполнения кода Java SE 16.

Полезные ресурсы:

  1. Ошибка JDK#JDK-8180352
  2. Вызов метода varargs Java с одним нулевым аргументом?
person Arvind Kumar Avinash    schedule 30.01.2021
comment
Означает ли это впоследствии, что .toList() никогда не сможет вернуть список, содержащий null, а .collect(toList() может? то есть Stream.of(null, null).toList() бросает, Stream.of(null, null).collect(toList()) возвращает? - person knittl; 30.01.2021
comment
IIRC, Collectors.toList() также не гарантирует, что мы получим изменяемый список. Это просто происходит в версиях Java, которые мы видели до сих пор. - person Ole V.V.; 30.01.2021
comment
@ОлеВ.В. - Правильный. В статье, указанной в моем ответе, это упоминается как: 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. - person Arvind Kumar Avinash; 30.01.2021
comment
хорошо, что вы отредактировали, я понятия не имею, как мой комментарий был удален, хотя :| - person Eugene; 31.01.2021
comment
Автору ничего не адресовано и, возможно, что-то для нового вопроса, но не странно ли, что он возвращает List, но вы не можете использовать половину методов в этом интерфейсе? Тогда на самом деле этот интерфейс не реализуется, не так ли? - person Caramiriel; 03.02.2021
comment
@Caramiriel Интерфейс List был специально разработан с рядом необязательных методов, то есть методов, которые реализующие классы не должны реализовывать и которые не реализуются во многих реализациях. Вы не первый, кто удивляется. - person Ole V.V.; 04.02.2021
comment
На самом деле неверно утверждать, что collect(toList()) возвращает изменяемый список; Спецификация ясно указывает на то, что она не гарантирует изменчивость возвращаемого списка. Текущая реализация случайно возвращает ArrayList прямо сейчас, но спецификация была специально написана, чтобы позволить это изменить. Если вам нужен изменяемый список, используйте toCollection(ArrayList::new). - person Brian Goetz; 19.02.2021
comment
Очень жаль, что List не разделили на ReadableList с get() и т.д. и WritableList с set(), add() и т.д., но уже поздно исправлять. (примечание: изначально я пришел сюда, чтобы предложить toCollection(ArrayList::new), который я всегда использую, когда мне нужен изменяемый список, но Сам Человек опередил меня. ????) - person Clement Cherlin; 22.02.2021
comment
Новый метод Stream.toList() не создает ни неизменяемый список, ни ярлык для collect(toUnmodifiableList()), потому что toUnmodifiableList() не принимает пустые значения. Реализация Stream.toList() не ограничивается интерфейсом Collector; поэтому Stream.toList() выделяет меньше памяти. blogs.oracle.com/javamagazine/hidden-gems-jdk16-jdk17- jep Можете ли вы подтвердить приведенные выше утверждения в статье? - person deepakl.2000; 28.07.2021

Вот небольшая таблица, в которой суммированы различия между Stream.collect(Collectors.toList()), Stream.collect(Collectors.toUnmodifiableList()) и Stream.toList():

  • collect(toList()): Гарантирует неизменность - Нет; Разрешить пустые значения — Да
  • collect(toUnmodifiableList()): Гарантия неизменяемости - Да; Разрешить пустые значения — Нет
  • toList(): Гарантия неизменяемости - Да; Разрешить пустые значения — Да
person ZhekaKozlov    schedule 01.02.2021
comment
Новый метод Stream.toList() не создает ни неизменяемый список, ни ярлык для collect(toUnmodifiableList()), потому что toUnmodifiableList() не принимает пустые значения. Реализация Stream.toList() не ограничивается интерфейсом Collector; поэтому Stream.toList() выделяет меньше памяти. Это делает оптимальным использование, когда размер потока известен заранее. blogs.oracle.com/javamagazine/hidden-gems-jdk16-jdk17- jep Можете ли вы подтвердить приведенные выше утверждения - person deepakl.2000; 28.07.2021