Collectors.toList() 和 Stream.toList() 这两种方法的区别是什么

在 Java 中,stream().collect(Collectors.toList()) 和 stream().toList() 都是将 Stream 转换为 List 的方法,但它们在 实现方式、性能、可变性 等方面有显著区别。以下是详细对比:


1. stream().collect(Collectors.toList())

List<String> collect = a.stream().collect(Collectors.toList());

特点:

  1. 返回的 List 类型:
    • 具体实现取决于 JDK 版本:
      • JDK 8~15:返回 ArrayList(默认)。
      • JDK 16+:可能返回不可变 List(取决于 Collectors.toList() 的实现优化)。
    • 但 官方未明确保证返回类型,理论上可能是任意 List 实现。
  2. 可变性:
    • 返回的 List 通常是可变的(如 ArrayList),可以调用 add()remove() 等方法。
    • 但如果 JDK 内部优化(如 JDK 16+ 的某些情况),可能返回不可变 List
  3. 性能:
    • 需要额外创建 Collectors.toList() 收集器对象,会有轻微性能开销。
    • 底层仍然是调用 Stream 的收集逻辑,与 toList() 差异不大。
  4. 线程安全:
    • 返回的 List 不是线程安全的(除非显式包装为 Collections.synchronizedList)。

2. stream().toList()

List<String> strings = a.stream().toList();

 

特点:

  1. 返回的 List 类型:
    • JDK 16+ 明确保证:返回一个不可变 List(具体实现可能是 List.of() 或内部优化类)。
    • 如果流为空,返回一个空的不可变 List(而非 null)。
  2. 可变性:
    • 返回的 List 严格不可变,调用 add()remove() 会抛出 UnsupportedOperationException
  3. 性能:
    • 比 Collectors.toList() 更高效,因为:
      • 直接内联到 Stream API 的实现中,无需额外收集器对象。
      • 针对不可变性做了优化(如共享数据、延迟拷贝)。
  4. 线程安全:
    • 不可变 List 本质是线程安全的(只要不尝试修改)。

3. 核心区别总结

以下是它们的详细对比:

 

特性Collectors.toList()Stream.toList()
返回类型 返回一个 List<T>,通常是 ArrayList 返回一个不可变的 List<T>
可变性 生成的列表是可变的,可以添加、删除元素 生成的列表是不可变的,任何修改操作都会抛出 UnsupportedOperationException
空流处理 返回一个空的可变列表 返回一个空的不可变列表
方法类型 是一个收集器(Collector),用于 collect() 方法 是 Stream 接口的终端操作方法
Java 版本支持 Java 8 及以上 Java 16 及以上
性能 略低,因为涉及收集器的创建和处理 略高,直接生成不可变列表
使用示例 list.stream().collect(Collectors.toList()); list.stream().toList();

代码示例

 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamToListComparison {
    public static void main(String[] args) {
        // 源列表
        List<String> sourceList = List.of("apple", "banana", "cherry");
        
        // 使用 Collectors.toList()
        List<String> mutableList = sourceList.stream()
                .collect(Collectors.toList());
        System.out.println("Mutable List: " + mutableList);
        
        // 尝试修改可变列表
        mutableList.add("date");
        System.out.println("After adding to mutable list: " + mutableList);
        
        // 使用 Stream.toList()
        List<String> immutableList = sourceList.stream()
                .toList();
        System.out.println("Immutable List: " + immutableList);
        
        // 尝试修改不可变列表(会抛出异常)
        try {
            immutableList.add("elderberry");
        } catch (UnsupportedOperationException e) {
            System.out.println("Exception caught: " + e.getMessage());
        }
        
        // 处理空流的情况
        List<String> emptyMutableList = new ArrayList<>().stream()
                .collect(Collectors.toList());
        System.out.println("Empty mutable list size: " + emptyMutableList.size());
        
        List<String> emptyImmutableList = new ArrayList<>().stream()
                .toList();
        System.out.println("Empty immutable list size: " + emptyImmutableList.size());
    }
}

输出结果

Mutable List: [apple, banana, cherry]
After adding to mutable list: [apple, banana, cherry, date]
Immutable List: [apple, banana, cherry]
Exception caught: Unsupported operation
Empty mutable list size: 0
Empty immutable list size: 0

 


 

4. 如何选择?

  1. 需要可变 List
    • 使用 collect(Collectors.toList())(但注意 JDK 16+ 可能返回不可变列表)。
    • 或显式创建 ArrayList
      List<String> list = new ArrayList<>(a.stream().toList()); // 复制为可变列表
  2. 不需要修改 List(推荐):
    • 优先使用 toList()(JDK 16+),性能更好且代码简洁。
    • 如果兼容 JDK 8~15,仍需用 Collectors.toList(),但需注意可变性。
  3. 明确需要不可变 List
    • 使用 List.copyOf(stream().toList()) 或 Stream 的 toList()(JDK 16+)。

推荐使用场景

  • 需要可变列表时:使用 Collectors.toList(),例如你需要后续对列表进行增删操作。
  • 需要不可变列表时:使用 Stream.toList(),可以避免意外修改,提高代码安全性。
  • Java 版本考虑:如果项目使用的是 Java 15 或更低版本,只能使用 Collectors.toList()
  • 性能敏感场景:在性能关键的代码中,优先考虑 Stream.toList(),因为它通常更高效。

 

posted @ 2025-07-23 09:35  r1-12king  阅读(126)  评论(0)    收藏  举报