Java Stream所有方法详解 - CSDN博客

2026-01-01 04:55:11 · 作者: AI Assistant · 浏览: 1

在Java 8引入Stream API后,开发人员有了更简洁、更易读的方式来处理集合数据。本文将深入解析Stream API的核心概念和高级用法,涵盖创建方法、中间操作、终止操作、常用收集器、性能优化技巧及线程安全处理等方面,帮助开发者更好地掌握这一强大工具。

Java的Stream API是Java 8引入的一个功能强大的工具,它为集合操作提供了一种函数式编程的方式。通过Stream,开发者可以以声明式的方式处理数据流,实现过滤、映射、归约等操作。本篇文章将详细介绍Stream API的创建方法、中间和终止操作、收集器使用、高级用法、性能建议以及线程安全处理,帮助初学者和初级开发者全面掌握这一技术。

Stream 创建方法

Stream API提供了多种创建流的方式,适用于不同的数据源。下面是几种常见的创建方法:

  1. stream():从集合创建顺序流。例如,list.stream()会生成一个按集合顺序处理的流。
  2. parallelStream():创建并行流,适合大数据量处理。例如,list.parallelStream()生成一个并行处理的流。
  3. Stream.of():由一组元素创建流。例如,Stream.of(1, 2, 3)会生成一个包含这三个元素的流。
  4. Arrays.stream():由数组创建流。例如,Arrays.stream(arr)可以将一个整型数组转换为IntStream。
  5. Stream.iterate():生成无限流。例如,Stream.iterate(0, n -> n+2).limit(5)会生成从0开始,每次加2,限制为5个元素的流。
  6. Stream.generate():生成无限流。例如,Stream.generate(Math::random).limit(5)会生成5个随机数的流。

这些创建方法使得开发者可以根据不同的场景灵活地使用Stream API。

Stream 中间操作(Intermediate Operations)

中间操作返回新的Stream,允许链式调用,这些操作本身不会产生结果,而是构建一个处理链。常见的中间操作包括:

  1. filter(Predicate):过滤元素。例如,stream.filter(x -> x > 5)会过滤出所有大于5的元素。
  2. map(Function):元素映射。例如,stream.map(x -> x * 2)会将每个元素乘以2。
  3. flatMap(Function):展开嵌套流。例如,stream.flatMap(list -> list.stream())可以将嵌套的集合展开为一个流。
  4. distinct():去重。例如,stream.distinct()会移除重复的元素。
  5. sorted():排序。例如,stream.sorted()会对流中的元素进行排序。
  6. peek(Consumer):元素遍历但不终止流。例如,stream.peek(System.out::println)会在遍历元素时打印出来。
  7. limit(long n):截取前n个元素。例如,stream.limit(3)会取前三个元素。
  8. skip(long n):跳过前n个元素。例如,stream.skip(2)会跳过前两个元素。

中间操作的特点是惰性求值,只有在终止操作执行时才会实际处理数据,这有助于提高性能和资源利用效率。

Stream 终止操作(Terminal Operations)

终止操作会产生结果或副作用,流被“消费”后不可再用。常见的终止操作包括:

  1. forEach(Consumer):遍历元素。例如,stream.forEach(System.out::println)会打印出每个元素。
  2. collect(Collector):收集结果。例如,stream.collect(Collectors.toList())会将流中的元素收集到一个List中。
  3. toArray():转数组。例如,stream.toArray()会将流转换为数组。
  4. reduce(BinaryOperator):规约(聚合)。例如,stream.reduce((a, b) -> a + b)会将流中的元素相加。
  5. count():计数。例如,stream.count()会统计流中的元素数量。
  6. anyMatch(Predicate):是否有任意元素匹配。例如,stream.anyMatch(x -> x > 5)会判断是否有元素大于5。
  7. allMatch(Predicate):是否所有元素都匹配。例如,stream.allMatch(x -> x > 5)会判断所有元素是否大于5。
  8. noneMatch(Predicate):是否没有元素匹配。例如,stream.noneMatch(x -> x > 5)会判断是否有元素不大于5。
  9. findFirst():查找第一个元素。例如,stream.findFirst()会返回流中的第一个元素。
  10. findAny():查找任意元素。例如,stream.findAny()会返回流中的任意一个元素。
  11. min(Comparator):最小值。例如,stream.min(Comparator.naturalOrder())会找到最小的元素。
  12. max(Comparator):最大值。例如,stream.max(Comparator.naturalOrder())会找到最大的元素。

终止操作的特点是会立即执行并产生结果,操作完成后流将被关闭,不能再进行其他操作。

常用 Collector 收集器

Collector 是 Stream API 中用于收集结果的重要工具,提供了多种收集方式以满足不同的需求。以下是一些常用的 Collector:

  1. Collectors.toList():将流收集为List。例如,stream.collect(Collectors.toList())会将元素收集到一个List中。
  2. Collectors.toSet():将流收集为Set。例如,stream.collect(Collectors.toSet())会将元素收集到一个Set中。
  3. Collectors.toMap():将流收集为Map。例如,stream.collect(Collectors.toMap(x -> x, x -> x*2))会将元素收集到一个Map中,其中键为元素本身,值为元素乘以2。
  4. Collectors.joining():字符串拼接。例如,stream.collect(Collectors.joining(","))会将元素拼接成一个字符串,元素之间用逗号分隔。
  5. Collectors.groupingBy():分组。例如,stream.collect(Collectors.groupingBy(x -> x%2))会按元素的奇偶性分组。
  6. Collectors.partitioningBy():分区。例如,stream.collect(Collectors.partitioningBy(x -> x > 3))会将元素分为两组,分别满足条件和不满足条件。
  7. Collectors.counting():计数。例如,stream.collect(Collectors.counting())会统计元素数量。
  8. Collectors.summingInt():求和。例如,stream.collect(Collectors.summingInt(x -> x))会求出所有元素的和。
  9. Collectors.averagingInt():平均值。例如,stream.collect(Collectors.averagingInt(x -> x))会求出所有元素的平均值。
  10. Collectors.maxBy():最大值。例如,stream.collect(Collectors.maxBy(Comparator.naturalOrder()))会找到最大的元素。
  11. Collectors.minBy():最小值。例如,stream.collect(Collectors.minBy(Comparator.naturalOrder()))会找到最小的元素。

这些收集器使得开发者可以更灵活地处理数据,提高代码的可读性和效率。

示例代码

以下是一些示例代码,帮助理解Stream API的使用:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);

// 过滤、映射、收集
List<Integer> result = list.stream()
    .filter(x -> x > 3)
    .map(x -> x * 2)
    .collect(Collectors.toList()); // [8, 10, 12]

// 分组
Map<Boolean, List<Integer>> partitioned = list.stream()
    .collect(Collectors.partitioningBy(x -> x % 2 == 0)); 

// 计数
long count = list.stream().filter(x -> x > 3).count();

// 求和
int sum = list.stream().reduce(0, Integer::sum);

这些示例展示了如何使用Stream API进行过滤、映射、分组和计数等操作。

Stream 高级用法

  1. 多级分组:可以通过嵌套使用groupingBy来实现多级分组。例如,users.stream().collect(Collectors.groupingBy(User::getCity, Collectors.groupingBy(User::getAge)))会先按城市分组,再按年龄分组。
  2. 分区与分组的区别:分区仅适用于布尔条件,分组可以有多个分组键。
  3. 自定义收集器:可以通过Collector.of自定义收集逻辑。例如,Collector<Integer, ?, Set<Integer>> toCustomSet = Collector.of(HashSet::new, Set::add, (left, right) -> { left.addAll(right); return left; });会自定义一个收集器,将元素收集到一个Set中。
  4. 并行流:并行流适合CPU密集型任务,但需要注意线程安全。例如,list.parallelStream().filter(x -> x > 3).forEach(System.out::println)会并行处理元素。
  5. Optional 结合 Stream:使用Optional可以处理可能为空的结果。例如,Optional<Integer> first = list.stream().filter(x -> x > 3).findFirst();会找到第一个大于3的元素,使用ifPresent来处理结果。

常见陷阱与注意点

在使用Stream API时,需要注意以下几个陷阱:

  1. 流只能用一次:流操作后已关闭,不能再操作,否则会抛出异常。
  2. 修改集合元素:不建议在流操作中直接修改原集合元素,推荐返回新集合。
  3. 空指针问题:Stream操作前应确保集合非null,推荐使用Optional.ofNullable(list).orElse(Collections.emptyList()).stream()来处理可能为null的集合。
  4. 性能问题distinct()sorted()等操作会增加性能消耗,需要谨慎使用。

Stream API 性能建议

  1. 减少中间操作链长度:必要时合并操作,以提高性能。
  2. 大数据量推荐并行流:并行流适合处理大数据量,但需测试线程安全和实际效率。
  3. 优先使用基本类型流:使用IntStreamLongStreamDoubleStream可减少装箱/拆箱开销。
  4. 避免在流中写复杂逻辑:建议拆分为多个方法,提高可读性。

Stream 相关扩展

Java提供了三种基本类型流:IntStreamLongStreamDoubleStream,它们避免了自动装箱/拆箱,提高了性能。创建方式包括:

  1. IntStream.of():由一组整数元素创建流。例如,IntStream intStream = IntStream.of(1, 2, 3, 4);
  2. LongStream.range():生成一个范围流。例如,LongStream longStream = LongStream.range(1, 10);生成从1到9的流。
  3. DoubleStream.generate():生成一个随机数流。例如,DoubleStream doubleStream = DoubleStream.generate(Math::random).limit(5);

这些基本类型流在处理大量数据时能显著提高性能。

流的收集与转换技巧

  1. 转换为Map,处理key重复:使用toMap时,需要提供一个合并函数来处理重复的key。例如,Map<String, Integer> map = list.stream().collect(Collectors.toMap(User::getName, User::getScore, Integer::sum));
  2. 收集为不可变集合:使用collectingAndThen可以收集为不可变集合。例如,List<String> unmodifiableList = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
  3. 多字段分组:可以通过嵌套使用groupingBy实现多字段分组。例如,Map<String, Map<Integer, List<User>>> group = users.stream().collect(Collectors.groupingBy(User::getCity, Collectors.groupingBy(User::getAge)));

Stream 与并发/线程安全

并行流(parallelStream())会自动分片并行处理,适合无状态、无副作用的操作。使用toConcurrentMapConcurrentHashMap可以确保线程安全。例如,ConcurrentMap<String, Integer> concurrentMap = list.parallelStream().collect(Collectors.toConcurrentMap(User::getName, User::getScore));

Stream 排序技巧

  1. 单字段排序:使用Comparator.comparing进行单字段排序。例如,list.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
  2. 多字段排序:可以通过thenComparing实现多字段排序。例如,list.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(Collectors.toList());
  3. 逆序排序:使用reversed()进行逆序排序。例如,list.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());

Stream 实用代码片段

  1. 找出重复元素:使用HashSetfilter找出重复元素。例如,Set<Integer> seen = new HashSet<>(); Set<Integer> duplicates = list.stream().filter(n -> !seen.add(n)).collect(Collectors.toSet());会找出所有重复的元素。

通过以上详细讲解,读者可以全面了解Java Stream API的核心概念和高级用法,从而在实际开发中更加高效和灵活地处理集合数据。掌握这些技巧不仅能提升代码质量,还能优化性能,特别是在处理大数据量或复杂业务逻辑时。

关键字列表:Java, Stream API, 集合操作, 中间操作, 终止操作, 收集器, 并行流, 线程安全, 性能优化, 函数式编程