Collect a Java Stream to an Immutable Collection – 收集一个Java流到一个不可变的集合

最后修改: 2017年 8月 3日

中文/混合/英文(键盘快捷键:t)

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.

一如既往,本文的完整源代码在GitHub上。它们按Java版本分为第3-4节第2.2节第2.3节的示例。