1. Introduction
1.介绍
We often wish to convert a Java Stream into a collection. This usually results in a mutable collection, but we can customize it.
我们经常希望将Java Stream转换成一个集合。这通常会产生一个易变的集合,但我们可以对其进行自定义。
In this short tutorial, we’re going to take a close look at how to collect a Java Stream to an immutable collection – first using plain Java, and then using the Guava library.
在这个简短的教程中,我们将仔细看看如何将一个Java流收集到一个不可变的集合–首先使用普通Java,然后使用Guava库。
2. Using Standard Java
2.使用标准Java
2.1. Using Java’s toUnmodifiableList
2.1.使用Java的toUnmodifiableList
Starting with Java 10, we can use the toUnmodifiableList method from Java’s Collectors class:
从Java 10开始,我们可以使用Java的toUnmodifiableList类的Collectors方法。
List<String> givenList = Arrays.asList("a", "b", "c");
List<String> result = givenList.stream()
.collect(toUnmodifiableList());
By using this method, we get a List implementation that doesn’t support null values from Java’s ImmutableCollections:
通过使用这个方法,我们得到了一个List实现,它不支持Java的ImmutableCollections中的null值。
class java.util.ImmutableCollections$ListN
2.2. Using Java’s collectingAndThen
2.2.使用Java的collectingAndThen
The collectingAndThen method from Java’s Collectors class accepts a Collector and a finisher Function. This finisher is applied to the result returned from the Collector:
Java的Collectors类中的collectingAndThen方法接受一个Collector和一个finisherFunction。这个finisher被应用于从Collector>返回的结果:。
List<String> givenList = Arrays.asList("a", "b", "c");
List<String> result = givenList.stream()
.collect(collectingAndThen(toList(), ImmutableList::copyOf));
System.out.println(result.getClass());
With this approach, since we can’t use the toCollection Collector directly, we need to collect elements into a temporary list. Then, we construct an immutable list from it.
通过这种方法,由于我们不能直接使用toCollection Collector,我们需要将元素收集到一个临时列表中。然后,我们从中构造一个不可变的列表。
2.3. Using Stream.toList() Method
2.3.使用Stream.toList()方法
Java 16 introduces a new method on Stream API called toList(). This handy method returns an unmodifiable List containing the stream elements:
Java 16在Stream API上引入了一个新方法,名为toList()。这个方便的方法返回一个包含流元素的不可修改的List 。
@Test
public void whenUsingStreamToList_thenReturnImmutableList() {
List<String> immutableList = Stream.of("a", "b", "c", "d").toList();
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
immutableList.add("e");
});
}
As we can see in the unit test, Stream.toList() returns an immutable list. So, trying to add a new element to the list will simply lead to UnsupportedOperationException.
正如我们在单元测试中看到的,Stream.toList() 返回一个不可变的列表。因此,试图向列表添加新的元素将仅仅导致UnsupportedOperationException。
Please bear in mind that the new Stream.toList() method is slightly different from the existing Collectors.toList() as it returns an unmodifiable list.
请记住,新的Stream.toList()方法与现有的Collectors.toList()略有不同,因为它返回一个无法修改的列表。
3. Building a Custom Collector
3.建立一个自定义的收集器
We also have the option to implement a custom Collector.
我们还可以选择实现一个自定义的Collector。
3.1. A Basic Immutable Collector
3.1.一个基本的不可变的Collector
To achieve this, we can use the static Collector.of method:
为了实现这一点,我们可以使用静态的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);
}
We can use this function just like any built-in Collector:
我们可以像任何内置的Collector一样使用这个函数。
List<String> givenList = Arrays.asList("a", "b", "c", "d");
List<String> result = givenList.stream()
.collect(MyImmutableListCollector.toImmutableList());
Finally, let’s check the output type:
最后,让我们检查一下输出类型。
class java.util.Collections$UnmodifiableRandomAccessList
3.2. Making the MyImmutableListCollector Generic
3.2.让MyImmutableListCollector具有通用性
Our implementation has one limitation – it always returns an immutable instance backed by an ArrayList. However, with a slight improvement, we can make this collector return a user-specified type:
我们的实现有一个限制–它总是返回一个由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);
}
So now, instead of determining the Supplier in the method implementation, we’re requesting the Supplier from the user:
所以现在,我们不是在方法实现中确定Supplier,而是向用户请求Supplier。
List<String> givenList = Arrays.asList("a", "b", "c", "d");
List<String> result = givenList.stream()
.collect(MyImmutableListCollector.toImmutableList(LinkedList::new));
Also, we’re using the LinkedList instead of ArrayList.
另外,我们使用的是LinkedList而不是ArrayList。
class java.util.Collections$UnmodifiableList
This time, we got UnmodifiableList instead of UnmodifiableRandomAccessList.
这一次,我们得到了UnmodifiableList而不是UnmodifiableRandomAccessList。
4. Using Guava’s Collectors
4.使用Guava的Collectors
In this section, we’re going to use the Google Guava library to drive some of our examples:
在本节中,我们将使用Google Guava库来驱动我们的一些示例。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
Starting with Guava 21, every immutable class comes with an accompanying Collector that’s as easy to use as Java’s standard Collectors:
从Guava 21开始,每个不可变的类都有一个附带的Collector,它和Java的标准Collectors一样容易使用:。
List<Integer> list = IntStream.range(0, 9)
.boxed()
.collect(ImmutableList.toImmutableList());
The resulting instance is the RegularImmutableList:
产生的实例是RegularImmutableList。
class com.google.common.collect.RegularImmutableList
5. Conclusion
5.结论
In this short article, we’ve seen various ways to collect a Stream into an immutable Collection.
在这篇短文中,我们看到了将Stream收集到一个不可变的Collection的各种方法。
As always, the full source code of this article is over on GitHub. They’re separated by Java version into examples for sections 3-4, section 2.2, and section 2.3.