跳转到内容

Java Stream 流式编程总结

Java 8 Stream API 常用操作速查与实战


一、Stream 基础概念

1.1 什么是 Stream

Stream 是 Java 8 引入的函数式数据处理 API,对集合数据进行声明式的、流水线式的操作,支持链式调用惰性求值

// 传统方式
List<String> result = new ArrayList<>();
for (String s : list) {
    if (s.length() > 3) {
        result.add(s.toUpperCase());
    }
}

// Stream 方式
List<String> result = list.stream()
    .filter(s -> s.length() > 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

1.2 Stream 三要素

数据源  ──→  中间操作(可多个)  ──→  终端操作(仅一个)
(集合/数组)   (惰性求值,返回Stream)   (触发计算,返回结果)

类型

说明

示例

中间操作

返回 Stream,惰性不触发计算

filter, map, sorted, distinct, limit

终端操作

触发计算,关闭 Stream

collect, forEach, reduce, count, anyMatch


二、创建 Stream

// 1. 从集合创建
Stream<String> stream = list.stream();           // 串行流
Stream<String> parallelStream = list.parallelStream(); // 并行流

// 2. 从数组创建
IntStream intStream = Arrays.stream(new int[]{1, 2, 3});
Stream<String> strStream = Arrays.stream(new String[]{"a", "b"});

// 3. 直接创建
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5);
Stream<String> empty = Stream.empty();

// 4. 生成无限流
Stream<Double> randoms = Stream.generate(Math::random);
Stream<Integer> iterate = Stream.iterate(0, n -> n + 2);  // 0,2,4,6,...

// 5. 基本类型流(避免装箱)
IntStream intStream2 = IntStream.range(1, 100);    // 1~99
LongStream longStream = LongStream.rangeClosed(1, 100); // 1~100(含)
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

三、中间操作

3.1 filter —— 过滤

List<Integer> evens = Stream.of(1, 2, 3, 4, 5, 6)
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// [2, 4, 6]

3.2 map —— 映射/转换

// 提取 User 的 name 字段
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

// 类型转换
List<Integer> lengths = Stream.of("aa", "bbb", "cccc")
    .map(String::length)
    .collect(Collectors.toList());
// [2, 3, 4]

3.3 flatMap —— 扁平化

// 将多个 list 打平成一个
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4, 5)
);
List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// [1, 2, 3, 4, 5]

// 拆分字符串
List<String> words = Stream.of("hello world", "java stream")
    .flatMap(s -> Arrays.stream(s.split(" ")))
    .collect(Collectors.toList());
// [hello, world, java, stream]

3.4 distinct —— 去重

List<Integer> distinct = Stream.of(1, 2, 2, 3, 3, 3)
    .distinct()
    .collect(Collectors.toList());
// [1, 2, 3]

3.5 sorted —— 排序

// 自然排序
List<Integer> sorted = Stream.of(3, 1, 4, 1, 5)
    .sorted()
    .collect(Collectors.toList());

// 自定义排序
users.stream()
    .sorted(Comparator.comparing(User::getAge).reversed())
    .collect(Collectors.toList());

// 多条件排序
users.stream()
    .sorted(Comparator.comparing(User::getAge)
        .thenComparing(User::getName))
    .collect(Collectors.toList());

3.6 limit / skip —— 截取

List<Integer> top3 = Stream.of(1, 2, 3, 4, 5)
    .limit(3)
    .collect(Collectors.toList());
// [1, 2, 3]

List<Integer> skip2 = Stream.of(1, 2, 3, 4, 5)
    .skip(2)
    .collect(Collectors.toList());
// [3, 4, 5]

// 分页:第2页,每页3条
List<Integer> page2 = list.stream()
    .skip((2 - 1) * 3)
    .limit(3)
    .collect(Collectors.toList());

3.7 peek —— 调试窥探

List<String> result = Stream.of("a", "b", "c")
    .peek(s -> System.out.println("处理: " + s))
    .map(String::toUpperCase)
    .peek(s -> System.out.println("映射后: " + s))
    .collect(Collectors.toList());

四、终端操作

4.1 collect —— 收集(最常用)

// 收集为 List
List<String> list = stream.collect(Collectors.toList());

// 收集为 Set
Set<String> set = stream.collect(Collectors.toSet());

// 收集为 Map
Map<Integer, User> userMap = users.stream()
    .collect(Collectors.toMap(User::getId, Function.identity()));

// 收集为 Map(处理重复 key)
Map<String, User> map2 = users.stream()
    .collect(Collectors.toMap(
        User::getName,
        Function.identity(),
        (existing, replacement) -> replacement  // 保留后者
    ));

// 分组
Map<String, List<User>> groupByDept = users.stream()
    .collect(Collectors.groupingBy(User::getDept));

// 多级分组
Map<String, Map<String, List<User>>> twoLevel = users.stream()
    .collect(Collectors.groupingBy(
        User::getDept,
        Collectors.groupingBy(User::getGender)
    ));

// 分组计数
Map<String, Long> countByDept = users.stream()
    .collect(Collectors.groupingBy(User::getDept, Collectors.counting()));

// 分区(true/false 两组)
Map<Boolean, List<User>> adultPartition = users.stream()
    .collect(Collectors.partitioningBy(u -> u.getAge() >= 18));

// 拼接字符串
String joined = users.stream()
    .map(User::getName)
    .collect(Collectors.joining(", ", "[", "]"));
// [张三, 李四, 王五]

4.2 forEach —— 遍历

users.stream().forEach(System.out::println);

4.3 reduce —— 归约

// 求和
int sum = Stream.of(1, 2, 3, 4, 5)
    .reduce(0, Integer::sum);

// 求最大值
Integer max = Stream.of(1, 5, 3, 9, 2)
    .reduce(Integer.MIN_VALUE, Integer::max);

4.4 count —— 计数

long count = users.stream()
    .filter(u -> u.getAge() > 18)
    .count();

4.5 匹配(短路操作)

boolean allAdult = users.stream().allMatch(u -> u.getAge() >= 18);  // 全匹配
boolean anyMale  = users.stream().anyMatch(u -> "男".equals(u.getGender())); // 任一匹配
boolean noneNull = users.stream().noneMatch(u -> u.getName() == null); // 全不匹配

4.6 findFirst / findAny

// 取第一个元素
Optional<User> first = users.stream()
    .filter(u -> u.getAge() > 30)
    .findFirst();

// 取任意元素(并行流中更高效)
Optional<User> any = users.parallelStream()
    .filter(u -> u.getAge() > 30)
    .findAny();

五、数值流(减少装箱开销)

// 基本类型流
IntStream intStream = IntStream.range(1, 100);
LongStream longStream = LongStream.of(1L, 2L, 3L);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

// 常用方法
int sum = IntStream.rangeClosed(1, 100).sum();           // 求和
OptionalDouble avg = IntStream.range(1, 100).average();   // 平均值
OptionalInt max = IntStream.range(1, 100).max();          // 最大值

// 装箱:基本类型流 → 对象流
Stream<Integer> boxed = IntStream.range(1, 10).boxed();

// 拆箱:对象流 → 基本类型流
IntStream ages = users.stream().mapToInt(User::getAge);

六、Collectors 深入

// 聚合统计
IntSummaryStatistics stats = users.stream()
    .collect(Collectors.summarizingInt(User::getAge));
stats.getMax(); stats.getMin(); stats.getAverage(); stats.getSum(); stats.getCount();

// reducing 自定义归约
Integer totalAge = users.stream()
    .collect(Collectors.reducing(0, User::getAge, Integer::sum));

// collectingAndThen:收集后再处理
List<User> unmodifiable = users.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toList(),
        Collections::unmodifiableList
    ));

// mapping:在分组前做转换
Map<String, List<Integer>> deptAges = users.stream()
    .collect(Collectors.groupingBy(
        User::getDept,
        Collectors.mapping(User::getAge, Collectors.toList())
    ));

七、实战场景

7.1 List → Map

// id → User
Map<Integer, User> map = users.stream()
    .collect(Collectors.toMap(User::getId, u -> u));

// name → age
Map<String, Integer> nameAge = users.stream()
    .collect(Collectors.toMap(User::getName, User::getAge));

7.2 按条件分组

// 按年龄分组
Map<Integer, List<User>> byAge = users.stream()
    .collect(Collectors.groupingBy(User::getAge));

// 按年龄段分组
Map<String, List<User>> byAgeRange = users.stream()
    .collect(Collectors.groupingBy(u -> {
        if (u.getAge() < 18) return "未成年";
        else if (u.getAge() < 60) return "成年";
        else return "老年";
    }));

7.3 提取对象某字段去重

// 按 name 去重,保留第一个
List<User> distinctByName = users.stream()
    .collect(Collectors.toMap(
        User::getName,
        Function.identity(),
        (existing, replace) -> existing
    ))
    .values().stream()
    .collect(Collectors.toList());

7.4 排序取 Top N

List<User> top5 = users.stream()
    .sorted(Comparator.comparing(User::getScore).reversed())
    .limit(5)
    .collect(Collectors.toList());

7.5 求最大值/最小值对象

// 年龄最大的用户
Optional<User> oldest = users.stream()
    .max(Comparator.comparing(User::getAge));

// 分数最小的用户
Optional<User> youngest = users.stream()
    .min(Comparator.comparing(User::getScore));

7.6 条件统计

// 各部门人数
Map<String, Long> deptCount = users.stream()
    .collect(Collectors.groupingBy(User::getDept, Collectors.counting()));

// 各部门平均年龄
Map<String, Double> avgAgeByDept = users.stream()
    .collect(Collectors.groupingBy(
        User::getDept,
        Collectors.averagingInt(User::getAge)
    ));

7.7 合并多个 List

List<String> allNames = Stream.of(list1, list2, list3)
    .flatMap(Collection::stream)
    .collect(Collectors.toList());

八、Optional 与空值处理

// 安全获取第一个元素
String first = list.stream()
    .filter(s -> s.length() > 3)
    .findFirst()
    .orElse("默认值");

// 存在就处理
list.stream().findAny()
    .ifPresent(System.out::println);

// 抛异常
User user = users.stream()
    .filter(u -> u.getId() == 100)
    .findFirst()
    .orElseThrow(() -> new RuntimeException("用户不存在"));

九、并行流

// 创建并行流
users.parallelStream()

// 串行流 → 并行流
users.stream().parallel()

// 注意:
// 1. 数据量大时才有性能提升
// 2. 注意线程安全问题(避免共享可变状态)
// 3. 有顺序要求时慎用(findFirst vs findAny)

十、常用操作速查表

操作

类型

说明

filter

中间

条件过滤

map

中间

一对一转换

flatMap

中间

一对多转换+扁平化

distinct

中间

去重

sorted

中间

排序

limit(n)

中间

取前 n 个

skip(n)

中间

跳过前 n 个

peek

中间

调试窥探

collect

终端

收集为集合

forEach

终端

遍历

reduce

终端

归约聚合

count

终端

计数

anyMatch / allMatch / noneMatch

终端

短路匹配

findFirst / findAny

终端

查找元素

max / min

终端

最大/最小值

记忆口诀filter 过滤 map 转,flatMap 打平 sorted 排,distinct 去重 limit 截,skip 跳过 collect 收。