Skip to content

2026-08-29 · 集合的流水线处理:过滤、转换、排序、分组、收集,一条链子写完。

Java Stream 流

1. Stream 是什么

Stream(流):对集合数据做批量处理的"流水线"。不存储数据,只是把集合"流"过去,边流边处理。

原始数据 ──▶ [filter 过滤] ──▶ [map 转换] ──▶ [sorted 排序] ──▶ [collect 收集]
             每一站都是操作,最后收集成新结果

和 for 循环的区别:for 是"命令式"——你一步步告诉电脑怎么走;Stream 是"声明式"——你只说"过滤、转换、排序",细节它自己安排。

2. 创建 Stream 的三种方式

java
import java.util.*;
import java.util.stream.*;

// ① 集合 → stream(最常用)
List<String> list = List.of("a", "b", "c");
Stream<String> s1 = list.stream();

// ② 数组 → stream
String[] arr = {"a", "b"};
Stream<String> s2 = Arrays.stream(arr);

// ③ 直接造
Stream<String> s3 = Stream.of("a", "b", "c");

Stream 用完即弃(一次性的):流操作完就没了,想再用要重新从集合拿。不能"倒回去"。

3. 中间操作:过滤、转换、排序(可无限链)

中间操作只是"布置流水线",不真正执行,最后必须跟一个终结操作才跑起来。

3.1 filter:过滤(Predicate 判断)

java
List<Integer> nums = List.of(1, 2, 3, 4, 5, 6);

// 只要偶数
nums.stream()
    .filter(n -> n % 2 == 0)        // 留下满足条件的
    .forEach(System.out::println);  // 2 4 6

3.2 map:转换(Function 映射)

java
List<String> names = List.of("alice", "bob");

// 每个元素转大写,收集成新列表
List<String> upper = names.stream()
        .map(String::toUpperCase)   // 方法引用,等价于 s -> s.toUpperCase()
        .collect(Collectors.toList());
// [ALICE, BOB]

3.3 sorted:排序

java
List<Integer> nums = List.of(3, 1, 2);
nums.stream()
    .sorted()                             // 自然升序
    .sorted((a, b) -> b - a)              // 自定义:降序(Comparator Lambda)
    .forEach(System.out::println);        // 3 2 1

3.4 distinct:去重

java
List<Integer> nums = List.of(1, 2, 1, 3, 2);
nums.stream().distinct().forEach(System.out::println);  // 1 2 3

3.5 limit / skip:限量/跳过

java
// 取前 2 个 / 跳过前 1 个
nums.stream().limit(2);     // 1 2
nums.stream().skip(1);      // 2 3 4...

3.6 链起来用(核心体验)

java
// 需求:数字里过滤出偶数 → 转成字符串 → 排序 → 取前 2 个
List<Integer> nums = List.of(5, 2, 8, 1, 9, 4);

List<String> result = nums.stream()
        .filter(n -> n % 2 == 0)      // 2 8 4
        .map(n -> "数" + n)           // 数2 数8 数4
        .sorted()                     // 数2 数4 数8
        .limit(2)                     // 数2 数4
        .collect(Collectors.toList());

System.out.println(result);  // [数2, 数4]

这就是 Stream 的优势:一个需求一条链写完,不用写一堆 for + if + 临时变量。

4. 终结操作:真正执行(流到头了)

方法作用示例
forEach遍历执行list.stream().forEach(System.out::println)
collect收进新集合.collect(Collectors.toList())
count数个数list.stream().filter(...).count()
anyMatch有任意一个满足?.anyMatch(n -> n > 5)
allMatch全部满足?.allMatch(n -> n > 0)
noneMatch一个都不满足?.noneMatch(n -> n < 0)
min/max最大最小值.mapToInt(Integer::intValue).max()
java
List<Integer> nums = List.of(1, 2, 3, 4);

boolean hasBig = nums.stream().anyMatch(n -> n > 3);  // true
long count = nums.stream().filter(n -> n % 2 == 0).count(); // 2

Collectors 常用收法:

java
// 收集成 List(最常用)
List<String> list = stream.collect(Collectors.toList());

// 收集成 Set(自动去重)
Set<String> set = stream.collect(Collectors.toSet());

// 拼接字符串
String joined = stream.collect(Collectors.joining(", "));  // "a, b, c"

// 统计:个数/和/平均
Collectors.counting();
Collectors.summingInt(s -> s.length());

5. 分组:按条件把元素分类(嵌套 Map 实战)

按用户年龄分组,结果就是 Map<Integer, List<Person>>(整数年龄 → 这个年龄的一群人)——这正是集合嵌套最典型的场景:

java
class Person { String name; int age; }

List<Person> people = List.of(
        new Person("张三", 20),
        new Person("李四", 30),
        new Person("王五", 20)
);

// 按 age 分组:key=年龄,value=这个年龄的所有人
Map<Integer, List<Person>> byAge = people.stream()
        .collect(Collectors.groupingBy(p -> p.age));

// {20=[张三, 王五], 30=[李四]}  ← 嵌套结构:Map 里套 List

嵌套集合的读写(开发中常见):

java
// 创建:里面再放一个集合当 value
Map<String, List<String>> map = new HashMap<>();
map.put("水果", new ArrayList<>(List.of("苹果", "香蕉")));

// 读取
List<String> fruits = map.get("水果");      // [苹果, 香蕉]
String first = map.get("水果").get(0);      // 苹果(先按 key 拿 List,再按下标拿元素)

// 遍历嵌套 Map:两层循环
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
    System.out.println(entry.getKey());          // 组名
    for (String item : entry.getValue()) {       // 组内元素
        System.out.println("  " + item);
    }
}

嵌套就一句话:value 本身又是集合Map<K, List<V>>Map<K, Map<K2, V>> 都一样,一层层剥开读。

6. 什么时候用 Stream,什么时候用 for

场景用哪个
过滤/转换/排序/分组一条龙Stream(清晰、少代码)
遍历时还要改其他变量、中断退出for 循环
代码要给别人看、团队不熟 Streamfor 循环(可读性优先)
大数据量(百万级)Stream 并行流 parallelStream() 可以试,但先别碰

判断标准:一条链能表达完就用 Stream;逻辑复杂、要打断、要中间状态,回到 for。

7. 本篇小结

  1. Stream = 集合的流水线:中间操作(filter/map/sorted/distinct/limit)链式布置,终结操作(forEach/collect/count)才执行
  2. 中间操作接的都是函数式接口:filter→Predicate、map→Function、sorted→Comparator
  3. collect(Collectors.toList()) 是最常用收尾;groupingBy 一键分组
  4. 集合嵌套 = value 是集合:Map<String, List<T>> 一层层剥开
  5. 简单链用 Stream,复杂流程用 for,不要强行使用 Stream