Streams API 是 8 引入的一个强大特性,用于以声明式方式处理数据集合。它允许你以高效且易读的方式对集合数据进行复杂的操作,如过滤、映射、排序和聚合。
1. Stream 基础概念
1.1 什么是 Stream
Stream 不是数据结构,而是对数据源(集合、数组、I/O资源等)的元素序列进行函数式操作的一种抽象。主要特点包括:
不存储数据:只是从数据源传递数据
不改变源数据:操作会产生新Stream,不影响原始数据
惰性执行:中间操作是惰性的,只有终端操作才会触发实际计算
可消费性:Stream只能被消费一次
1.2 Stream 操作类型
操作类型 特点 示例方法
中间操作(Intermediate) 返回Stream,可链式调用 filter(), map(), sorted()
终端操作(Terminal) 产生结果或副作用,结束Stream forEach(), collect(), reduce()
2. 创建 Stream
2.1 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream(); // 顺序流
Stream<String> parallelStream = list.parallelStream(); // 并行流
2.2 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
2.3 使用Stream.of()
Stream<String> stream = Stream.of("a", "b", "c");
2.4 其他创建方式
// 无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
Stream<Double> randomStream = Stream.generate(Math::random);
// 空流
Stream<String> emptyStream = Stream.empty();
// 基本类型流
IntStream intStream = IntStream.range(1, 10);
3. 常用中间操作
3.1 filter() - 过滤
List<String> filtered = list.stream()
.filter(s -> s.startsWith("a"))
.collect(Collectors.toList());
3.2 map() - 映射转换
List<Integer> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
3.3 flatMap() - 扁平化映射
List<String> words = Arrays.asList("Hello", "World");
List<String> letters = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.collect(Collectors.toList());
// 结果: ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]
3.4 distinct() - 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> distinct = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3]
3.5 sorted() - 排序
List<String> sorted = list.stream()
.sorted()
.collect(Collectors.toList());
// 自定义排序
List<String> customSorted = list.stream()
.sorted((s1, s2) -> s2.compareTo(s1))
.collect(Collectors.toList());
3.6 peek() - 调试查看
List<String> result = list.stream()
.filter(s -> s.length() > 2)
.peek(System.out::println) // 调试用
.map(String::toUpperCase)
.collect(Collectors.toList());
3.7 limit() 和 skip() - 限制和跳过
List<Integer> limited = IntStream.range(1, 100)
.skip(20) // 跳过前20个
.limit(10) // 只取10个
.boxed()
.collect(Collectors.toList());
4. 常用终端操作
4.1 forEach() - 遍历
list.stream().forEach(System.out::println);
4.2 collect() - 收集为集合
List<String> collectedList = stream.collect(Collectors.toList());
Set<String> collectedSet = stream.collect(Collectors.toSet());
4.3 toArray() - 转为数组
String[] array = stream.toArray(String[]::new);
4.4 reduce() - 归约
Optional<String> concatenated = stream.reduce((s1, s2) -> s1 + s2);
// 带初始值
Integer sum = numbers.stream().reduce(0, (a, b) -> a + b);
4.5 min()/max()/count() - 统计
Optional<String> min = stream.min(Comparator.naturalOrder());
Optional<String> max = stream.max(Comparator.reverseOrder());
long count = stream.filter(s -> s.startsWith("a")).count();
4.6 anyMatch()/allMatch()/noneMatch() - 匹配检查
boolean anyStartsWithA = stream.anyMatch(s -> s.startsWith("a"));
boolean allStartsWithA = stream.allMatch(s -> s.startsWith("a"));
boolean noneStartsWithZ = stream.noneMatch(s -> s.startsWith("z"));
4.7 findFirst()/findAny() - 查找元素
Optional<String> first = stream.findFirst();
Optional<String> any = stream.findAny(); // 并行流中可能更快
5. 收集器(Collectors)高级用法
5.1 转换为特定集合
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
TreeSet<String> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
5.2 joining() - 字符串连接
String joined = stream.collect(Collectors.joining(", "));
// 结果: "a, b, c"
5.3 groupingBy() - 分组
Map<Integer, List<String>> groupByLength = stream
.collect(Collectors.groupingBy(String::length));
// 多级分组
Map<Integer, Map<Character, List<String>>> multiLevelGroup = stream
.collect(Collectors.groupingBy(String::length,
Collectors.groupingBy(s -> s.charAt(0))));
5.4 partitioningBy() - 分区
Map<Boolean, List<String>> partition = stream
.collect(Collectors.partitioningBy(s -> s.length() > 3));
// 结果: {false=["a", "b"], true=["long", "word"]}
5.5 summarizingInt/Double/Long - 统计摘要
IntSummaryStatistics stats = stream
.collect(Collectors.summarizingInt(String::length));
// 可获取count, sum, min, average, max
5.6 mapping() - 下游收集器
Map<Integer, List<Character>> firstCharByLength = stream
.collect(Collectors.groupingBy(
String::length,
Collectors.mapping(s -> s.charAt(0), Collectors.toList())
);
6. 并行流(Parallel Streams)
6.1 创建并行流
Stream<String> parallelStream = list.parallelStream();
Stream<String> parallel = stream.parallel();
6.2 注意事项
确保操作是无状态的
避免共享可变状态
考虑操作成本与并行开销
某些操作(如findAny)在并行流中表现更好
// 并行流示例
long count = list.parallelStream()
.filter(s -> s.length() > 3)
.count();
7. 原始类型流(IntStream, LongStream, DoubleStream)
7.1 创建方式
IntStream intStream = IntStream.of(1, 2, 3);
IntStream range = IntStream.range(1, 10); // 1-9
IntStream rangeClosed = IntStream.rangeClosed(1, 10); // 1-10
7.2 常用操作
int sum = intStream.sum();
OptionalDouble avg = intStream.average();
OptionalInt max = intStream.max();
7.3 与对象流转换
// 对象流 -> IntStream
IntStream lengths = list.stream().mapToInt(String::length);
// IntStream -> 对象流
Stream<Integer> boxed = intStream.boxed();
8. 最佳实践
优先使用方法引用:使代码更简洁
.map(String::length) // 优于 .map(s -> s.length())
避免副作用:保持函数纯度
合理使用并行流:数据量大且操作耗时时使用
重用Stream:Stream只能消费一次,需要时重新创建
注意性能:某些操作(sorted, distinct)可能很昂贵
优先选择专用收集器:
.collect(Collectors.toList()) // 优于 .collect(ArrayList::new, List::add, List::addAll)
Streams API 提供了一种高效、声明式处理数据的方式,可以大幅简化集合操作代码,提高可读性和维护性。