mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
693 字
2 分钟
Java Stream API详解
2026-02-03

Stream API是Java 8里我最喜欢的新特性之一,它让Java从命令式编程迈向了函数式编程的大门。刚开始用的时候觉得filter、map、reduce这些操作很陌生,但一旦上手了,写集合处理代码简直停不下来——太优雅了。

Java Stream API详解#

Stream是什么?#

Stream就是一个数据流处理管道。它不是数据结构,不存数据,只是对数据源(集合、数组等)进行一系列操作。

几个关键点

  • 中间操作是惰性的:不执行终端操作,中间操作不会触发
  • Stream只能消费一次:用完就关闭了,不能重复使用
  • 不修改数据源:Stream操作不会修改原始集合

创建Stream#

// 从集合
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream(); // 并行流
// 从数组
String[] array = {"a", "b", "c"};
Stream<String> arrayStream = Arrays.stream(array);
// 直接创建
Stream<String> ofStream = Stream.of("a", "b", "c");
Stream<Double> randomStream = Stream.generate(Math::random); // 无限流
Stream<Integer> evenStream = Stream.iterate(0, n -> n + 2); // 无限流
// 限制无限流的大小
randomStream.limit(5).forEach(System.out::println);
// 从文件
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
lines.forEach(System.out::println);
}

中间操作(返回Stream,可链式调用)#

filter —— 筛选#

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filtered = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
// 结果: [Alice, Charlie, David]

map —— 映射(转换)#

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 结果: [5, 3, 7]

flatMap —— 扁平化映射#

把嵌套结构拍平——比如List<List>转成List

List<List<String>> lists = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d"),
Arrays.asList("e", "f")
);
List<String> flattened = lists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果: [a, b, c, d, e, f]

distinct —— 去重#

List<Integer> numbers = Arrays.asList(1, 2, 3, 2, 1, 4, 5, 4);
List<Integer> distinct = numbers.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]

sorted —— 排序#

List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
List<Integer> sorted = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]
// 自定义排序
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> desc = names.stream()
.sorted((s1, s2) -> s2.compareTo(s1))
.collect(Collectors.toList());
// 结果: [Charlie, Bob, Alice]

peek —— 调试用#

想在流处理过程中看一眼中间结果,用peek:

List<Integer> result = numbers.stream()
.peek(n -> System.out.println("处理前: " + n))
.map(n -> n * 2)
.peek(n -> System.out.println("处理后: " + n))
.collect(Collectors.toList());

limit 和 skip#

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> first5 = numbers.stream().limit(5).collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]
List<Integer> after5 = numbers.stream().skip(5).collect(Collectors.toList());
// 结果: [6, 7, 8, 9, 10]

终端操作(触发计算,返回结果)#

forEach —— 遍历#

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

collect —— 收集到集合#

List<String> list = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
Set<String> set = names.stream()
.collect(Collectors.toSet());
Map<String, Integer> map = names.stream()
.collect(Collectors.toMap(name -> name, String::length));
// 指定具体的集合类型
LinkedList<String> linkedList = names.stream()
.collect(Collectors.toCollection(LinkedList::new));

reduce —— 归约#

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);
// 或指定初始值
int sumWithInit = numbers.stream().reduce(0, Integer::sum);
// 求最大值
Optional<Integer> max = numbers.stream().reduce(Integer::max);
// 字符串连接
String joined = names.stream().collect(Collectors.joining(", "));
// 结果: Alice, Bob, Charlie

count —— 计数#

long count = names.stream()
.filter(name -> name.length() > 3)
.count();

匹配操作#

// anyMatch:有任何一个满足就返回true
boolean hasBob = names.stream().anyMatch(name -> name.equals("Bob"));
// allMatch:所有都满足才返回true
boolean allPositive = numbers.stream().allMatch(n -> n > 0);
// noneMatch:没有满足的返回true
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0);

查找操作#

Optional<String> first = names.stream().findFirst();
Optional<String> any = names.stream().findAny(); // 并行流时更高效
// 处理Optional
String result = first.orElse("默认值");
first.ifPresent(System.out::println);

min / max#

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

分组和分区#

groupingBy —— 分组#

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
// 按名字长度分组
Map<Integer, List<String>> byLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// 结果: {3=[Bob, Eve], 5=[Alice, David], 7=[Charlie]}
// 分组后统计数量
Map<Integer, Long> countByLength = names.stream()
.collect(Collectors.groupingBy(String::length, Collectors.counting()));
// 结果: {3=2, 5=2, 7=1}

partitioningBy —— 分区(true/false)#

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// 结果: {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}

并行Stream#

数据量大、计算密集型任务可以用并行流提升性能:

// 并行求和
int sum = numbers.parallelStream()
.reduce(0, Integer::sum);

注意

  • 并行流不保证处理顺序
  • 操作必须无副作用、线程安全
  • 小数据集用并行流反而更慢(线程切换有开销)
  • 默认线程数 = CPU核心数

实战贴士#

  1. Stream不能重复使用:存到变量里想用两次,第二次调用就抛IllegalStateException了。每次用都从数据源重新创建。
  2. 注意副作用:不要在Stream操作里修改外部变量,用collect代替forEach+add
  3. 选择合适的短路操作anyMatchfindFirst这些操作找到结果就停止遍历,比filter+count效率高。
  4. 并行流不是银弹:数据量小的时候串行更快,IO密集型任务也不适合并行流。
  5. 调试用peek:想看一眼Stream中间处理结果,用peek(System.out::println)

常见坑#

  • Stream重复使用stream.filter(...); stream.map(...); 第二次调用会抛异常。正确做法是链式调用。
  • forEach里修改外部变量list.stream().forEach(x -> result.add(x)) 在多线程下有并发问题。用collect
  • 并行流顺序问题parallelStream().forEach() 不保证顺序,要保证顺序用forEachOrdered()
  • 无限流没limitStream.generate(Math::random) 不加limit会无限生成下去,内存撑爆。
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

部分信息可能已经过时

目录