Collecting Stream Elements into a List in Java – 在Java中把流元素收集到一个列表中

最后修改: 2021年 9月 18日

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

1. Overview

1.概述

In this tutorial, we’ll look at different methods to get a List from a Stream. We’ll also discuss the differences among them and when to use which method.

在本教程中,我们将探讨从Stream中获取List的不同方法。我们还将讨论它们之间的区别以及何时使用哪种方法。

2. Collecting Stream Elements into a List

2.将流元素收集到一个列表中

Getting a List from a Stream is the most used terminal operation of the Stream pipeline. Before Java 16, we used to invoke the Stream.collect() method and pass it to a Collector as an argument to gather the elements into. The Collector itself was created by calling the Collectors.toList() method.

Stream中获取ListStream管道中最常用的终端操作。在Java 16之前,我们习惯于调用Stream.collect()方法,并将其作为参数传递给Collector来收集元素。Collector本身是通过调用Collectors.toList()方法创建的。

However, there have been change requests for a method to get a List directly from a Stream instance. After the Java 16 release, we can now invoke toList(), a new method directly on the Stream, to get the List. Libraries like StreamEx also provide a convenient way to get a List directly from a Stream.

然而,有人提出更改请求,要求提供一种方法,以便直接从Stream实例中获得List在Java 16发布后,我们现在可以调用toList(),一个直接在Stream上的新方法,以获得List。像StreamEx这样的库也提供了一种方便的方法来直接从Stream中获得List

We can accumulate Stream elements into a List by using:

我们可以通过使用以下方法将Streamelements累积到List中。

We’ll work with the methods in the chronological order of their release.

我们将按照这些方法发布的时间顺序进行工作。

3. Analyzing the Lists

3.分析名单

Let’s first create the lists from the methods described in the previous section. After that, let’s analyze their properties.

让我们首先从上一节描述的方法中创建列表。之后,让我们分析一下它们的属性。

We’ll use the following Stream of country codes for all the examples:

我们将在所有的例子中使用以下的国家代码。

Stream.of(Locale.getISOCountries());

3.1. Creating Lists

3.1.创建列表

Now, we’ll create a List from the given Stream of country codes using the different methods:

现在,我们将使用不同的方法从给定的Stream中创建一个List国家代码。

First, let’s create a List using Collectors:toList():

首先,让我们使用Collectors:toList()创建一个List

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

After that, let’s collect it using Collectors.toUnmodifiableList():

之后,让我们用 Collectors.toUnmodifiableList()来收集它。

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

Here, in these methods, we accumulate the Stream into a List through the Collector interface. This results in extra allocation and copying as we don’t work directly with the Stream.

在这里,在这些方法中,我们通过Collector接口Stream累积到List中。这导致了额外的分配和复制,因为我们没有直接与Stream合作。

Then, let’s repeat the collection with Stream.toList():

然后,让我们用 Stream.toList()重复收集。

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

Here, we get the List directly from the Stream, thus preventing extra allocation and copying.

在这里,我们直接从Stream获得List,从而避免了额外的分配和复制。

So, using toList() directly on the Stream is more concise, neat, convenient, and optimum when compared to the other two invocations.

因此,在Stream上直接使用toList(),与其他两个调用相比,更简洁、更整齐、更方便、更理想。

3.2. Examining the Accumulated Lists

3.2.检查累积的名单

Let’s begin with examining the type of List we created.

让我们首先检查一下我们创建的List的类型。

Collectors.toList(), collects the Stream elements into an ArrayList:

Collectors.toList(),将Stream元素收集到一个ArrayList

java.util.ArrayList

Collectors.toUnmodifiableList(), collects the Stream elements into an unmodifiable List.

Collectors.toUnmodifiableList(),将Stream元素收集成一个不可修改的List

java.util.ImmutableCollections.ListN

Stream.toList(), collects the elements into an unmodifiable List.

Stream.toList(),将元素收集到一个不可修改的List

java.util.ImmutableCollections.ListN

Though the current implementation of the Collectors.toList() creates a mutable List, the method’s specification itself makes no guarantee on the type, mutability, serializability, or thread-safety of the List.

尽管当前Collectors.toList()的实现创建了一个可变的List,但该方法的规范本身并没有对List的类型、可变性、可序列化或线程安全做出保证。

On the other hand, both Collectors.toUnmodifiableList() and Stream.toList(), produce unmodifiable lists.

另一方面,Collectors.toUnmodifiableList()Stream.toList(),都产生不可修改的列表。

This implies that we can do operations like add and sort on the elements of Collectors.toList(), but not on the elements of Collectors.toUnmodifiableList() and Stream.toList()

这意味着我们可以对Collectors.toList()的元素进行添加和排序等操作,但不能对Collectors.toUnmodifiableList()Stream.toList()的元素进行操作。

3.3. Allowing Null Elements in the Lists

3.3.允许列表中的空元素

Although Stream.toList() produces an unmodifiable List, it is still not the same as Collectors.toUnmodifiableList(). This is because Stream.toList() allows the null elements and Collectors.toUnmodifiableList() doesn’t allow the null elements. However, Collectors.toList() allows the null elements.

尽管Stream.toList()产生了一个不可修改的List,它仍然与Collectors.toUnmodifiableList()不一样。这是因为Stream.toList()允许null元素,而Collectors.toUnmodifiableList()不允许null元素。然而,Collectors.toList()允许null元素。

Collectors.toList() doesn’t throw an Exception when a Stream containing null elements is collected:

Collectors.toList() 在收集到含有null元素的Stream时,不会抛出Exception

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

Collectors.toUnmodifiableList() throws a NulPointerException when we collect a Stream containing null elements:

Collectors.toUnmodifiableList()当我们收集一个包含null元素的Stream时,会抛出NulPointerException

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

Stream.toList() doesn’t throw a NulPointerException when we try to collect a Stream containing null elements:

Stream.toList() 当我们试图收集一个包含null元素的Stream时,不会抛出一个NulPointerException

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

Therefore, this is something to watch out for when migrating our code from Java 8 to Java 10 or Java 16. We can’t blindly use Stream.toList() in place of Collectors.toList() or Collectors.toUnmodifiableList().

因此,当我们将代码从Java 8迁移到Java 10或Java 16时,这是需要注意的问题。我们不能盲目地使用Stream.toList()来代替Collectors.toList() Collectors.toUnmodifiableList()。

3.4. Summary of Analysis

3.4.分析总结

The following table summarizes the differences and similarities of the lists from our analysis:

下表总结了我们分析得出的名单的差异和相似之处。

4. When to Use Different toList() Methods

4.何时使用不同的toList()方法

The main objective of adding Stream.toList() is to reduce the verboseness of the Collector API.

添加Stream.toList()的主要目的是为了减少Collector API的动词。

As shown previously, using the Collectors methods for getting Lists is very verbose. On the other hand, using the Stream.toList() method makes code neat and concise.

如前所示,使用Ccollectors方法来获取Lists是非常冗长的。另一方面,使用Stream.toList()方法可以使代码变得整齐和简明。

Nevertheless, as seen in earlier sections, Stream.toList() can’t be used as a shortcut to Collectors.toList() or Collectors.toUnmodifiableList().

尽管如此,正如在前面的章节中所看到的,Stream.toList() 不能作为Collectors.toList() Collectors.toUnmodifiableList()的捷径。

Secondly, the Stream.toList() uses less memory because its implementation is independent of the Collector interface. It accumulates the Stream elements directly into the List. So, in case we know the size of the stream in advance, it will be optimum to use Stream.toList().

其次,Stream.toList()使用的内存更少,因为它的实现是独立于Collector接口的。它将Stream元素直接累积到List中。因此,如果我们事先知道流的大小,使用Stream.toList()将是最佳选择。

Thirdly, we know that the Stream API provides the implementation only for the toList() method. It doesn’t contain similar methods for getting a map or a set. So, in case we want a uniform approach to get any converters like list, map, or set, we’ll continue to use the Collector API. This will also maintain consistency and avoid confusion.

第三,我们知道Stream API只提供了toList()方法的实现。它不包含用于获取map或set的类似方法。因此,如果我们想用统一的方法来获取任何转换器,如list、map或set,我们将继续使用Collector API。这也将保持一致性,避免混淆。

Lastly, if we’re using versions lower than Java 16, we have to continue to use Collectors methods.

最后,如果我们使用低于Java 16的版本,我们必须继续使用Collectors方法。

The following table summarizes the optimum usage of the given methods:

下表总结了所给方法的最佳用法。

5. Conclusion

5.结论

In this article, we analyzed the three most popular ways of getting a List from a Stream. Then, we looked at the main differences and similarities. And, we also discussed how and when to use these methods.

在这篇文章中,我们分析了从Stream获得List的三种最流行的方法。然后,我们看了主要的差异和相似之处。而且,我们还讨论了如何以及何时使用这些方法。

As always, the source code for the examples used in this article is available over on GitHub.

一如既往,本文中所使用的示例的源代码可在GitHub上获取