1. Введение
Мы часто хотим преобразовать Java Stream
в коллекцию. Обычно это приводит к изменяемой коллекции, но мы можем настроить ее.
В этом кратком руководстве мы подробно рассмотрим, как собрать Java Stream в неизменяемую коллекцию — сначала с помощью обычной Java, а затем с помощью библиотеки Guava.
2. Использование стандартной Java
2.1. Использование toUnmodifiedList в Java
Начиная с Java 10, мы можем использовать метод toUnmodifiableList
из класса Collectors Java:
List<String> givenList = Arrays.asList("a", "b", "c");
List<String> result = givenList.stream()
.collect(toUnmodifiableList());
Используя этот метод, мы получаем реализацию List
, которая не поддерживает нулевые
значения из ImmutableCollections
Java :
class java.util.ImmutableCollections$ListN
2.2. Использование Java collectAndThen
Метод collectAndThen из класса
Collectors
в Java принимает Collector
и Finisher
Function
. Этот финишер
применяется к результату, возвращаемому сборщиком:
List<String> givenList = Arrays.asList("a", "b", "c");
List<String> result = givenList.stream()
.collect(collectingAndThen(toList(), ImmutableList::copyOf));
System.out.println(result.getClass());
При таком подходе, поскольку мы не можем напрямую использовать сборщик toCollection
, нам нужно собирать элементы во временный список. Затем мы создаем из него неизменяемый список.
2.3. Использование метода Stream.toList()
Java 16 представляет новый метод Stream API, который называется toList().
Этот удобный метод возвращает неизменяемый список
, содержащий элементы потока :
@Test
public void whenUsingStreamToList_thenReturnImmutableList() {
List<String> immutableList = Stream.of("a", "b", "c", "d").toList();
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
immutableList.add("e");
});
}
Как видно из модульного теста, Stream.toList()
возвращает неизменяемый список .
Таким образом, попытка добавить новый элемент в список просто приведет к UnsupportedOperationException .
Имейте в виду, что новый метод Stream.toList()
немного отличается от существующего метода Collectors.toList()
, поскольку он возвращает неизменяемый список.
3. Создание собственного коллектора
У нас также есть возможность реализовать собственный Collector
.
3.1. Базовый неизменяемый коллектор
Для этого мы можем использовать статический метод Collector.of :
public static <T> Collector<T, List<T>, List<T>> toImmutableList() {
return Collector.of(ArrayList::new, List::add,
(left, right) -> {
left.addAll(right);
return left;
}, Collections::unmodifiableList);
}
Мы можем использовать эту функцию так же, как любой встроенный Collector
:
List<String> givenList = Arrays.asList("a", "b", "c", "d");
List<String> result = givenList.stream()
.collect(MyImmutableListCollector.toImmutableList());
Наконец, давайте проверим тип вывода:
class java.util.Collections$UnmodifiableRandomAccessList
3.2. Создание универсального объекта MyImmutableListCollector
У нашей реализации есть одно ограничение — она всегда возвращает неизменяемый экземпляр, поддерживаемый ArrayList
. Однако с небольшим улучшением мы можем заставить этот сборщик возвращать указанный пользователем тип:
public static <T, A extends List<T>> Collector<T, A, List<T>> toImmutableList(
Supplier<A> supplier) {
return Collector.of(
supplier,
List::add, (left, right) -> {
left.addAll(right);
return left;
}, Collections::unmodifiableList);
}
Итак, теперь вместо определения Поставщика
в реализации метода мы запрашиваем Поставщика
у пользователя:
List<String> givenList = Arrays.asList("a", "b", "c", "d");
List<String> result = givenList.stream()
.collect(MyImmutableListCollector.toImmutableList(LinkedList::new));
Кроме того, мы используем LinkedList
вместо ArrayList
.
class java.util.Collections$UnmodifiableList
На этот раз мы получили UnmodifiableList
вместо UnmodifiedRandomAccessList
.
4. Использование коллектора
Guava
В этом разделе мы собираемся использовать библиотеку Google Guava для управления некоторыми из наших примеров:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
Начиная с Guava 21, каждый неизменяемый класс поставляется с сопутствующим Collector
, который так же прост в использовании, как и стандартный Collector
s в Java :
List<Integer> list = IntStream.range(0, 9)
.boxed()
.collect(ImmutableList.toImmutableList());
Результирующий экземпляр — это RegularImmutableList
:
class com.google.common.collect.RegularImmutableList
5. Вывод
В этой короткой статье мы рассмотрели различные способы собрать поток
в неизменяемую коллекцию
.
Как всегда, полный исходный код этой статьи находится на GitHub. Они разделены по версии Java на примеры для разделов 3-4 , раздела 2.2 и раздела 2.3 .