Перейти к основному содержимому

Сбор элементов потока в список в Java

· 4 мин. чтения

Упражнение: Сложение двух чисел

Даны два неотрицательный целых числа в виде непустых связных списков. Их цифры хранятся в обратном порядке. И каждый елемент списка содержить ровно одну цифру. Сложите эти два числа и верните сумму в виде связного списка ...

ANDROMEDA

1. Обзор

В этом руководстве мы рассмотрим различные методы получения списка из потока . Мы также обсудим различия между ними и когда какой метод использовать.

2. Сбор элементов потока в список

Получение списка из потока — наиболее часто используемая терминальная операция конвейера потока . До Java 16 мы использовали метод Stream.collect() и передавали его коллектору в качестве аргумента для сбора элементов. Сам коллектор был создан путем вызова метода Collectors.toList() .

Однако были запросы на изменение метода для получения списка непосредственно из экземпляра Stream . После выпуска Java 16 теперь мы можем вызывать toList() , новый метод непосредственно в Stream , чтобы получить List . Такие библиотеки, как StreamEx, также предоставляют удобный способ получить список непосредственно из потока .

Мы можем накапливать элементы Stream в список , используя:

  • Stream.collect(Collectors.toList()) : Начиная с Java 8
  • Stream.collect( Collectors.toUnmodifiableList() ) : начиная с Java 10
  • Stream.toList() : начиная с Java 16

Мы будем работать с методами в хронологическом порядке их выпуска.

3. Анализ списков

Давайте сначала создадим списки из методов, описанных в предыдущем разделе. После этого проанализируем их свойства.

Мы будем использовать следующий поток кодов стран для всех примеров:

Stream.of(Locale.getISOCountries());

3.1. Создание списков

Теперь мы создадим список из заданного потока кодов стран, используя различные методы:

Во-первых, давайте создадим список , используя Collectors:toList() :

List<String> result = Stream.of(Locale.getISOCountries()).collect(Collectors.toList());

После этого соберем его с помощью Collectors.toUnmodifiedList() :

List<String> result = Stream.of(Locale.getISOCountries()).collect(Collectors.toUnmodifiableList());

Вот в этих методах мы накапливаем Stream в List через интерфейс Collector . Это приводит к дополнительному выделению и копированию, поскольку мы не работаем напрямую с потоком.

Затем повторим сбор с помощью Stream.toList() :

List<String> result = Stream.of(Locale.getISOCountries()).toList();

Здесь мы получаем список непосредственно из потока, что предотвращает дополнительное выделение и копирование.

Таким образом, использование toList() непосредственно в потоке является более кратким, аккуратным, удобным и оптимальным по сравнению с двумя другими вызовами.

3.2. Изучение накопленных списков

Давайте начнем с изучения типа списка , который мы создали.

Collectors.toList() собирает элементы Stream в ArrayList :

java.util.ArrayList

Collectors.toUnmodifiedList() собирает элементы Stream в неизменяемый список .

java.util.ImmutableCollections.ListN

Stream.toList() собирает элементы в неизменяемый список .

java.util.ImmutableCollections.ListN

Хотя текущая реализация Collectors.toList() создает изменяемый List , сама спецификация метода не гарантирует тип, изменяемость, сериализуемость или потокобезопасность List.

С другой стороны, как Collectors.toUnmodifiableList() , так и Stream.toList() создают неизменяемые списки.

Это означает, что мы можем выполнять такие операции, как добавление и сортировка элементов Collectors.toList(), но не элементы Collectors.toUnmodifiableList() и Stream.toList() .

3.3. Разрешение пустых элементов в списках

Хотя Stream.toList() создает неизменяемый список , он все же не совпадает с Collectors.toUnmodifiableList(). Это связано с тем, что Stream.toList() допускает нулевые элементы, а Collectors.toUnmodifiableList() не допускает нулевые элементы. Однако Collectors.toList() допускает нулевые элементы.

Collectors.toList() не генерирует исключение при сборе потока , содержащего нулевые элементы:

Assertions.assertDoesNotThrow(() -> {
Stream.of(null,null).collect(Collectors.toList());
});

Collectors.toUnmodifiedList() генерирует исключение NulPointerException , когда мы собираем поток , содержащий нулевые элементы:

Assertions.assertThrows(NullPointerException.class, () -> {
Stream.of(null,null).collect(Collectors.toUnmodifiableList());
});

Stream.toList() не генерирует исключение NulPointerException , когда мы пытаемся собрать поток , содержащий нулевые элементы:

Assertions.assertDoesNotThrow(() -> {
Stream.of(null,null).toList();
});

Поэтому при переносе нашего кода с Java 8 на Java 10 или Java 16 следует обращать на это внимание. Мы не можем слепо использовать Stream.toList() вместо Collectors.toList() или Collectors.toUnmodifiableList().

3.4. Резюме анализа

В следующей таблице приведены различия и сходства списков из нашего анализа:

./57a848e147084aa7e4316687fdb0cb90.png

4. Когда использовать разные методы toList()

Основная цель добавления Stream.toList() — уменьшить многословность API Collector .

Как было показано ранее, использование методов Collectors для получения List очень многословно. С другой стороны, использование метода Stream.toList() делает код аккуратным и лаконичным.

Тем не менее, как было показано в предыдущих разделах, Stream.toList() нельзя использовать в качестве ярлыка для Collectors.toList() или Collectors.toUnmodifiableList() .

Во-вторых, Stream.toList() использует меньше памяти, поскольку его реализация не зависит от интерфейса Collector . Он накапливает элементы Stream непосредственно в List . Итак, если мы заранее знаем размер потока, оптимальным будет использование Stream.toList().

В-третьих, мы знаем, что Stream API предоставляет реализацию только для метода toList() . Он не содержит аналогичных методов для получения карты или набора. Итак, если нам нужен единый подход для получения любых преобразователей, таких как список, карта или набор, мы продолжим использовать Collector API. Это также позволит сохранить последовательность и избежать путаницы.

Наконец, если мы используем версии ниже Java 16, мы должны продолжать использовать методы Collectors .

В следующей таблице приведены оптимальные варианты использования данных методов:

./429b8e26e0319a52b3d795b839fca5d9.png

5. Вывод

В этой статье мы разобрали три самых популярных способа получения списка из потока . Затем мы рассмотрели основные различия и сходства. И мы также обсудили, как и когда использовать эти методы.

Как всегда, исходный код примеров, используемых в этой статье, доступен на GitHub .