为什么 Collectors.toList() 不能保证可变性

Ted*_*sai 2 java collections java-8 java-stream collectors

toList() 的实现明确返回一个 ArrayList,它确实保证了可变性:

public static <T>
Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>(ArrayList::new, List::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_ID);
}
Run Code Online (Sandbox Code Playgroud)

但是 toList 的 JavaDoc 是这样的:

/**
 * Returns a {@code Collector} that accumulates the input elements into a
 * new {@code List}. There are no guarantees on the type, mutability,
 * serializability, or thread-safety of the {@code List} returned; if more
 * control over the returned {@code List} is required, use {@link #toCollection(Supplier)}.
 *
 * @param <T> the type of the input elements
 * @return a {@code Collector} which collects all the input elements into a
 * {@code List}, in encounter order
 */
Run Code Online (Sandbox Code Playgroud)

为什么不能保证可变性?我是否遗漏了返回的集合可能不可变的情况?当返回的集合需要可变性时,使用 Collectors.toList() 是否安全?

Bas*_*que 7

因为作者是这么说的

\n
\n

为什么不能保证可变性?

\n
\n

因为该方法的设计者Collectors.toList选择了这样。

\n
\n

toList() 的实现显然返回一个 ArrayList

\n
\n

这个事实只是一个实现细节。

\n

该细节仅适用于您研究的一种实现的一个版本。其他版本和其他实现可能会使用ArrayList.

\n

在文档承诺的合同范围内,任何实施都可能发生变化。重要的是该文档所承诺的行为。

\n

引用Javadoc:

\n
\n

Collector返回将输入元素累积到新的 中的a List。不保证List返回值的类型、可变性、可序列化性或线程安全性;如果需要对返回的列表进行更多控制,请使用toCollection(Supplier).

\n
\n

对我来说似乎很清楚。我们已向您承诺,结果List可能是可变的,也可能是不可变的。你\xe2\x80\x99已被告知不要依赖任何一种情况。

\n

可修改

\n

如果您想要一个可变/可修改的List,请从收集器生成的列表中创建一个新的。将收集的列表传递给新列表对象的构造函数。

\n
List< Whatever > list = new ArrayList<>( collectorList ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

或者,正如上面的 Javadoc 所建议的,您可以传递Supplier渲染您选择的列表实现。称呼Collectors.toCollection。请参阅Baeldung.com 上教程的第 3.3 节。

\n

例如:

\n
toCollection( ArrayList :: new )\n
Run Code Online (Sandbox Code Playgroud)\n

不可修改

\n

如果您想要一个不可变/不可修改的列表,我建议致电List.copyOf.

\n
List< Whatever > list = List.copyOf( collectorList ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

该方法承诺返回:

\n
\n

\xe2\x80\xa6 一个不可修改的 List,包含给定 的元素(Collection按其迭代顺序)。

\n
\n

但有一个问题:不允许空值。包含空值的列表的两种替代方案:

\n\n
\n

顺便说一下,Java 21 带来了有序集合。所以更通用的接口ListSequencedCollection.

\n
SequencedCollection< Whatever > sequence = List.copyOf( collectorList ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

  • 如果你想要一个支持 `null` 的不可变列表,你可以简单地调用 [`toList()`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java `Stream` 上的 /util/stream/Stream.html#toList())(自 Java 16 起)。 (2认同)