Collectors.toUnmodifiableList 在 java-10

Collectors.toUnmodifiableList in java-10

如何使用 Collectors.toList/toSet/toMap 创建 Unmodifiable List/Set/Map,因为 toList(等)在文档中为:

There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned

java-10之前你必须提供一个FunctionCollectors.collectingAndThen,例如:

 List<Integer> result = Arrays.asList(1, 2, 3, 4)
            .stream()
            .collect(Collectors.collectingAndThen(
                    Collectors.toList(),
                    x -> Collections.unmodifiableList(x)));

使用 Java 10,这更容易,也更具可读性:

List<Integer> result = Arrays.asList(1, 2, 3, 4)
            .stream()
            .collect(Collectors.toUnmodifiableList());

在内部,它与 Collectors.collectingAndThen 相同,但 returns 是 Java 9 中添加的不可修改 List 实例。

另外要清除两个(collectingAndThen vs toUnmodifiableList)实现之间记录在案的差异:

The Collectors.toUnmodifiableList would return a Collector that disallows null values and will throw NullPointerException if it is presented with a null value.

static void additionsToCollector() {
    // this works fine unless you try and operate on the null element
    var previous = Stream.of(1, 2, 3, 4, null)
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));

    // next up ready to face an NPE
    var current = Stream.of(1, 2, 3, 4, null).collect(Collectors.toUnmodifiableList());
}

而且,这是因为前者构造了一个 Collections.UnmodifiableRandomAccessList 的实例,而后者构造了一个 ImmutableCollections.ListN 的实例,它添加到 [=24] 的属性列表中=] 和 static factory methods.

Stream#toList

Java 16 adds a method on the Stream interface: toList()。引用 Java 文档:

The returned List is unmodifiable; calls to any mutator method will always cause UnsupportedOperationException to be thrown.

不仅比 Collectors 更方便,这种方法还有一些好处,比如在并行流上有更好的性能。

In particular with parallel() -- as it avoids result copying. Benchmark is a few simple ops on 100K elem stream of Long.

如需进一步阅读,请访问:http://marxsoftware.blogspot.com/2020/12/jdk16-stream-to-list.html

在简历中 link 是这样写的。

Gotcha: It may be tempting to go into one's code base and use stream.toList() as a drop-in replacement for stream.collect(Collectors.toList()), but there may be differences in behavior if the code has a direct or indirect dependency on the implementation of stream.collect(Collectors.toList()) returning an ArrayList. Some of the key differences between the List returned by stream.collect(Collectors.toList()) and stream.toList() are spelled out in the remainder of this post.

The Javadoc-based documentation for Collectors.toList() states (emphasis added), "Returns a Collector that accumulates the input elements into a new List. There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned..." Although there are no guarantees regarding the "type, mutability, serializability, or thread-safety" on the List provided by Collectors.toList(), it is expected that some may have realized it's currently an ArrayList and have used it in ways that depend on the characteristics of an ArrayList

我的理解是 Stream.toList() 它将产生一个不变的列表。

Stream.toList() provides a List implementation that is immutable (type ImmutableCollections.ListN that cannot be added to or sorted) similar to that provided by List.of() and in contrast to the mutable (can be changed and sorted) ArrayList provided by Stream.collect(Collectors.toList()). Any existing code depending on the ability to mutate the ArrayList returned by Stream.collect(Collectors.toList()) will not work with Stream.toList() and an UnsupportedOperationException will be thrown.

Although the implementation nature of the Lists returned by Stream.collect(Collectors.toList()) and Stream.toList() are very different, they still both implement the List interface and so they are considered equal when compared using List.equals(Object)

并且此方法将允许空值,因此从 Java 16 开始我们将有一个

mutable/null-friendly----->Collectors.toList()
immutable/null-friendly--->Stream.toList()
immutable/null-hostile---->Collectors.toUnmodifiableList() //Naughty 

太棒了。

List/Set/Map.copyOf

你问过:

How do you create an Unmodifiable List/Set/Map

自 Java 10 起,只需将现有的 list/set/map 传递给:

这些静态方法分别return和unmodifiable List, unmodifiable Set, or unmodifiable Map。阅读那些链接的 Java 文档页面上的详细信息。

  • 不允许空值。
  • 如果传递的集合已经不可修改,则传递的集合只是 returned,没有进一步的工作,没有新的集合。

注意:如果使用方便的Stream#toList method in Java 16+ as described in ,这里这个解决方案没有意义,不需要调用List.copyOftoList 的结果已经不可修改。