Java 16 的 Stream.toList() 和 Stream.collect(Collectors.toList()) 的区别?

问题描述 投票:0回答:3

JDK 16 现在直接在

toList()
实例
上包含一个 
Stream
方法。在以前的 Java 版本中,您始终必须使用
collect
方法并提供
Collector
实例。

新方法明显减少了输入的字符。这两种方法可以互换吗?还是应该注意一些细微的差别?

var newList = someCollection.stream()
    .map(x -> mapX(x))
    .filter(x -> filterX(x))
    .toList();

// vs.

var oldList = someCollection.stream()
    .map(x -> mapX(x))
    .filter(x -> filterX(x))
    .collect(Collectors.toList());

(这个问题类似于 Stream.toList() 的性能会比 Collectors.toList() 更好,但关注的是行为而不是(仅)性能。)

java java-stream collectors java-16
3个回答
78
投票

一个区别是

Stream.toList()
提供了一个不可变的
List
实现(无法添加或排序的
ImmutableCollections.ListN
类型),类似于
List.of()
提供的实现,与可变(可以更改和排序) )
ArrayList
Stream.collect(Collectors.toList())
提供。

演示:

import java.util.stream.Stream;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = Stream.of("Hello").toList();
        System.out.println(list);
        list.add("Hi");
    }
}

输出:

[Hello]
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
    at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
    at Main.main(Main.java:8)

请查看这篇文章了解更多详情。

更新:

有趣的是,

Stream.toList()
成功返回了包含
null
的列表。

import java.util.stream.Stream;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Object> list = Stream.of(null, null).toList();
        System.out.println(list);
    }
}

输出:

[null, null]

另一方面,

List.of(null, null)
抛出
NullPointerException

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Object> list = List.of(null, null);
    }
}

输出:

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:208)
    at java.base/java.util.ImmutableCollections$List12.<init>(ImmutableCollections.java:453)
    at java.base/java.util.List.of(List.java:827)
    at Main.main(Main.java:5)

注意:我使用了openjdk-16-ea+34_osx-x64来编译和执行Java SE 16代码。

有用的资源:

  1. JDK 错误#JDK-8180352
  2. 使用单个 null 参数调用 Java varargs 方法?

53
投票

这里有一个小表格,总结了

Stream.collect(Collectors.toList())
Stream.collect(Collectors.toUnmodifiableList())
Stream.toList()
之间的区别:

方法 保证不可修改性 允许空值 自从
collect(toList())
没有 是的 Java 8
collect(toUnmodifiableList())
是的 没有 Java 10
toList()
是的 是的 Java 16

另一个小区别:

// Compiles
List<CharSequence> list = Stream.of("hello", "world").collect(toList());

// Error
List<CharSequence> list = Stream.of("hello", "world").toList();

11
投票

.collect(toList())
toList()
的行为在创建的列表中元素的子类型兼容性方面有所不同。

看看以下替代方案:

  • List<Number> old = Stream.of(0).collect(Collectors.toList());
    工作正常,尽管我们将Integer流收集到Number列表中。
  • List<Number> new = Stream.of(0).toList();
    是等效的 Java 16+ 版本,但它无法编译(
    cannot convert from List<Integer> to List<Number>
    ;至少在 ecj(Eclipse Java 编译器)中)。

至少有 2 个解决方法可以修复编译错误:

  • 显式转换为所需类型
    List<Number> fix1 = Stream.of(0).map(Number.class::cast).toList();
  • 在结果集合中允许子类型:
    List<? extends Number> fix2 = Stream.of(0).toList();

据我了解,根本原因如下:Java 16

toList()
的泛型类型 T 与 Stream 本身的泛型类型 T 相同。然而,Collectors.toList() 的泛型类型 T 是从赋值的左侧传播的。如果这两种类型不同,您在替换所有旧调用时可能会看到错误。

© www.soinside.com 2019 - 2024. All rights reserved.