Skip to content
清晨的一缕阳光
返回

Stream API 深度解析

Stream API 深度解析

Stream API 是 Java 8 引入的数据处理利器,让集合操作更简洁、更高效。

一、Stream 核心概念

1.1 什么是 Stream

Stream = 数据流 + 函数式操作

特点:
- 不存储数据
- 不改变源数据
- 懒加载(惰性求值)
- 可并行处理

1.2 Stream vs Collection

// Collection:存储数据
List<String> list = Arrays.asList("a", "b", "c");

// Stream:处理数据
Stream<String> stream = list.stream();
特性CollectionStream
存储存储数据不存储数据
修改可修改不可变
迭代外部迭代内部迭代
求值立即懒加载

二、创建 Stream

2.1 从集合创建

// Collection
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();

// Set
Set<String> set = new HashSet<>();
Stream<String> stream = set.stream();

// Map
Map<String, Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

2.2 从数组创建

String[] arr = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
Stream<String> stream2 = Stream.of(arr);

2.3 生成 Stream

// 空 Stream
Stream<String> empty = Stream.empty();

// 单个值
Stream<String> single = Stream.of("a");

// 多个值
Stream<String> multi = Stream.of("a", "b", "c");

// 无限流
Stream<Double> randoms = Stream.generate(Math::random);
Stream<Integer> naturals = Stream.iterate(0, n -> n + 1);

// 有限流
Stream<Integer> evens = Stream.iterate(0, n -> n + 2).limit(10);

2.4 从文件/IO 创建

// 文件行
Stream<String> lines = Files.lines(Paths.get("file.txt"));

// 目录
Stream<Path> paths = Files.list(Paths.get("/dir"));

// BufferedReader
Stream<String> lines = bufferedReader.lines();

三、中间操作

3.1 过滤

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

// filter
numbers.stream()
    .filter(n -> n % 2 == 0)
    .forEach(System.out::println); // [2, 4, 6]

// distinct
Stream.of(1, 2, 2, 3, 3, 3)
    .distinct()
    .forEach(System.out::println); // [1, 2, 3]

3.2 转换

// map
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println); // [A, B, C]

// flatMap
List<List<String>> nested = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d")
);
nested.stream()
    .flatMap(List::stream)
    .forEach(System.out::println); // [a, b, c, d]

// mapToInt/mapToLong/mapToDouble
list.stream()
    .mapToInt(String::length)
    .sum();

3.3 切片

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

// limit
numbers.stream().limit(3).forEach(System.out::println); // [1, 2, 3]

// skip
numbers.stream().skip(2).forEach(System.out::println); // [3, 4, 5]

// takeWhile(Java 9)
numbers.stream()
    .takeWhile(n -> n < 4)
    .forEach(System.out::println); // [1, 2, 3]

// dropWhile(Java 9)
numbers.stream()
    .dropWhile(n -> n < 4)
    .forEach(System.out::println); // [4, 5]

3.4 排序

List<String> list = Arrays.asList("c", "a", "b");

// 自然排序
list.stream()
    .sorted()
    .forEach(System.out::println); // [a, b, c]

// 自定义排序
list.stream()
    .sorted(Comparator.reverseOrder())
    .forEach(System.out::println); // [c, b, a]

// 比较器链式
list.stream()
    .sorted(Comparator.comparing(String::length)
        .thenComparing(Comparator.naturalOrder()))
    .forEach(System.out::println);

四、终端操作

4.1 查找

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

// anyMatch
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true

// allMatch
boolean allPositive = numbers.stream().allMatch(n -> n > 0); // true

// noneMatch
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0); // true

// findFirst
Optional<Integer> first = numbers.stream().findFirst();

// findAny(并行流更高效)
Optional<Integer> any = numbers.parallelStream().findAny();

4.2 归约

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

// reduce
Optional<Integer> sum = numbers.stream()
    .reduce((a, b) -> a + b);

// 带初始值
Integer sum2 = numbers.stream()
    .reduce(0, (a, b) -> a + b);

// 三元 reduce
Integer sum3 = numbers.stream()
    .reduce(0, 
        Integer::sum,           // 累加器
        Integer::sum            // 合并器(并行用)
    );

// 最大值/最小值
Optional<Integer> max = numbers.stream()
    .reduce(Integer::max);

Optional<Integer> min = numbers.stream()
    .reduce(Integer::min);

4.3 收集

List<String> list = Arrays.asList("a", "b", "c");

// toList
List<String> collected = list.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// toSet
Set<String> set = list.stream()
    .collect(Collectors.toSet());

// toMap
Map<String, Integer> map = list.stream()
    .collect(Collectors.toMap(
        s -> s,           // key
        String::length    // value
    ));

// 处理 key 冲突
Map<String, Integer> map2 = list.stream()
    .collect(Collectors.toMap(
        s -> s,
        String::length,
        (v1, v2) -> v1 + v2  // 冲突处理
    ));

// joining
String joined = list.stream()
    .collect(Collectors.joining(", ", "[", "]"));
// [a, b, c]

4.4 统计

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

// count
long count = numbers.stream().count();

// sum
int sum = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();

// average
OptionalDouble avg = numbers.stream()
    .mapToInt(Integer::intValue)
    .average();

// 统计对象
IntSummaryStatistics stats = numbers.stream()
    .mapToInt(Integer::intValue)
    .summaryStatistics();

System.out.println(stats.getCount());   // 5
System.out.println(stats.getSum());     // 15
System.out.println(stats.getAverage()); // 3.0
System.out.println(stats.getMin());     // 1
System.out.println(stats.getMax());     // 5

五、Collectors 详解

5.1 分组

List<Person> people = Arrays.asList(
    new Person("Alice", 20),
    new Person("Bob", 25),
    new Person("Charlie", 20)
);

// 单级分组
Map<Integer, List<Person>> byAge = people.stream()
    .collect(Collectors.groupingBy(Person::getAge));

// 多级分组
Map<Integer, Map<Character, List<Person>>> grouped = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.groupingBy(p -> p.getName().charAt(0))
    ));

// 分组后统计
Map<Integer, Long> countByAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.counting()
    ));

5.2 分区

// 分区(true/false)
Map<Boolean, List<Person>> partitioned = people.stream()
    .collect(Collectors.partitioningBy(p -> p.getAge() > 20));

// 分区后统计
Map<Boolean, Long> partitionCount = people.stream()
    .collect(Collectors.partitioningBy(
        p -> p.getAge() > 20,
        Collectors.counting()
    ));

5.3 下游收集器

// 分组后连接
Map<Integer, String> namesByAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.mapping(Person::getName, Collectors.joining(", "))
    ));
// {20=Alice, Charlie, 25=Bob}

// 分组后求平均
Map<Integer, Double> avgAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.averagingInt(Person::getAge)
    ));

六、并行流

6.1 创建并行流

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

// 方式 1:parallelStream
numbers.parallelStream()
    .forEach(System.out::println);

// 方式 2:parallel
numbers.stream()
    .parallel()
    .forEach(System.out::println);

// 方式 3:并行流转串行
numbers.parallelStream()
    .sequential()
    .forEach(System.out::println);

6.2 性能优化

// 适合并行处理的场景
// 1. 数据量大
// 2. CPU 密集型操作
// 3. 无序流

List<Long> numbers = LongStream.rangeClosed(1, 1_000_000_000L)
    .boxed()
    .collect(Collectors.toList());

// 串行
long start = System.currentTimeMillis();
numbers.stream()
    .mapToLong(Long::longValue)
    .sum();
System.out.println("Sequential: " + (System.currentTimeMillis() - start) + "ms");

// 并行
start = System.currentTimeMillis();
numbers.parallelStream()
    .mapToLong(Long::longValue)
    .sum();
System.out.println("Parallel: " + (System.currentTimeMillis() - start) + "ms");

6.3 注意事项

// ❌ 避免:有状态操作
numbers.parallelStream()
    .map(n -> {
        // 依赖外部状态
        return n + counter++;
    });

// ✅ 正确:无状态操作
numbers.parallelStream()
    .map(n -> n * 2);

// ❌ 避免:顺序敏感操作
numbers.parallelStream()
    .forEach(System.out::println); // 顺序不确定

// ✅ 正确:保证顺序
numbers.parallelStream()
    .forEachOrdered(System.out::println);

七、最佳实践

7.1 选择合适操作

// ❌ 低效
list.stream()
    .filter(s -> s != null)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// ✅ 高效
list.stream()
    .filter(Objects::nonNull)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

7.2 避免过早优化

// ❌ 过度使用并行流
list.parallelStream().map(...);

// ✅ 先串行,性能不足再并行
list.stream().map(...);

7.3 使用 Stream 简化代码

// 之前
List<String> result = new ArrayList<>();
for (String s : list) {
    if (s.startsWith("A")) {
        result.add(s.toUpperCase());
    }
}

// Stream
List<String> result = list.stream()
    .filter(s -> s.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

八、总结

Stream API 核心要点:

操作类型常用方法
中间操作filter, map, flatMap, sorted, limit, skip
终端操作forEach, collect, reduce, count, sum
收集器toList, toMap, groupingBy, joining
并行流parallelStream, sequential

Stream 让代码更简洁,但要注意性能,避免滥用并行流。


分享这篇文章到:

上一篇文章
索引优化实战案例
下一篇文章
慢查询日志与优化