Java Stream流完全指南:从入门到精通
Java Stream流完全指南:从入门到精通
作为一个写了多年Java代码的开发者,我想说Stream真的是Java 8给我们带来的最爽的特性之一。还记得以前处理集合数据时,写一堆for循环、if判断,代码又长又难看。自从有了Stream,代码变得优雅多了。今天就来跟大家聊聊Stream,保证让你从入门到精通!
第一章:为什么我们需要Stream?
1.1 传统代码的痛点
先来看个场景,假设我们有一个学生列表,需要找出所有成绩大于80分的学生姓名,并按成绩排序。传统写法是这样的:
List<Student> students = getStudents();
List<Student> filtered = new ArrayList<>();// 第一步:过滤
for (Student student : students) {if (student.getScore() > 80) {filtered.add(student);}
}// 第二步:排序
Collections.sort(filtered, new Comparator<Student>() {@Overridepublic int compare(Student s1, Student s2) {return Integer.compare(s2.getScore(), s1.getScore());}
});// 第三步:提取姓名
List<String> names = new ArrayList<>();
for (Student student : filtered) {names.add(student.getName());
}
看到没?为了实现一个简单的功能,我们需要:
- 创建临时集合
- 写多层for循环
- 代码分散在各处,逻辑不清晰
1.2 Stream的优雅方式
用Stream改写后是这样的:
List<String> names = students.stream().filter(s -> s.getScore() > 80).sorted((s1, s2) -> Integer.compare(s2.getScore(), s1.getScore())).map(Student::getName).collect(Collectors.toList());
是不是清爽多了?一条链式调用就搞定,代码读起来就像说话一样:
- 从学生流中
- 过滤出分数大于80的
- 按分数降序排序
- 提取姓名
- 收集成列表
这就是Stream的魅力!
第二章:Stream到底是什么?
2.1 核心概念
Stream不是数据结构,它不存储数据。Stream是对数据的一种描述和操作,就像流水线一样。
想象一下工厂的生产线:
- 数据源:原材料仓库(集合、数组等)
- 中间操作:各个加工工位(filter、map、sorted等)
- 终端操作:成品输出(collect、forEach等)
原材料 -> 筛选 -> 加工 -> 组装 -> 成品
List -> filter -> map -> sorted -> collect
2.2 Stream的三大特性
1. 不存储数据
Stream不会修改原始数据源,它只是对数据的一种视图。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().filter(n -> n > 3).collect(Collectors.toList());
// numbers 还是 [1, 2, 3, 4, 5],没有被改变
2. 函数式编程
Stream的操作是声明式的,我们告诉它"做什么",而不是"怎么做"。
// 传统:告诉计算机怎么做
for (int i = 0; i < list.size(); i++) {if (list.get(i) > 10) {// ...}
}// Stream:告诉计算机做什么
list.stream().filter(n -> n > 10)
3. 延迟执行
中间操作是懒加载的,只有遇到终端操作时才真正执行。这是性能优化的关键!
Stream<Integer> stream = list.stream().filter(n -> {System.out.println("filtering: " + n);return n > 10;});
// 这里不会打印任何东西,因为没有终端操作stream.collect(Collectors.toList());
// 现在才开始执行,打印filtering信息
第三章:创建Stream的N种方式
3.1 从集合创建
这是最常用的方式:
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();// 如果需要并行流
Stream<String> parallelStream = list.parallelStream();
3.2 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);// 也可以指定范围
Stream<String> stream2 = Arrays.stream(array, 0, 2); // "a", "b"
3.3 直接创建
// Stream.of() 创建固定元素的流
Stream<String> stream = Stream.of("a", "b", "c");// Stream.empty() 创建空流
Stream<String> empty = Stream.empty();// Stream.generate() 创建无限流(小心使用!)
Stream<Double> randoms = Stream.generate(Math::random);// Stream.iterate() 创建有规律的无限流
Stream<Integer> numbers = Stream.iterate(0, n -> n + 2); // 0, 2, 4, 6...
3.4 从文件创建
// 读取文件的每一行
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {lines.forEach(System.out::println);
}
3.5 从其他来源
// 从字符串创建字符流
IntStream chars = "hello".chars();// 从范围创建
IntStream range = IntStream.range(1, 10); // 1到9
IntStream rangeClosed = IntStream.rangeClosed(1, 10); // 1到10
第四章:中间操作 - 流水线的加工环节
中间操作会返回一个新的Stream,可以链式调用。记住:它们是懒加载的!
4.1 filter - 过滤
最常用的操作,筛选符合条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8