什么是Stream流?
Stream是Java 8中引入的一个强大的新特性,它允许我们以声明式的方式处理数据集合。想象一下,你有一箱苹果,想要找出所有红色的苹果并按大小排序。传统方式需要你一步步写循环和条件判断,而Stream则像是一条流水线,你只需要告诉它:"我要红色的苹果,按大小排序",它就会自动帮你完成。
为什么需要Stream?
在Stream出现之前,我们处理集合数据通常使用for循环或迭代器,这种方式称为
外部迭代——我们需要自己控制迭代过程。而Stream提供了
内部迭代,我们只需要关心"做什么",而不需要关心"怎么做"。
传统方式 vs Stream方式
// 传统方式:找出大于10的偶数并排序List<Integer> numbers = Arrays.asList(1, 2, 3, 12, 5, 6, 18, 7, 8);List<Integer> result = new ArrayList<>();for (Integer num : numbers) {
if (num % 2 == 0 && num > 10) {
result.add(num);
}}Collections.sort(result);// Stream方式:同样的功能List<Integer> result = numbers.stream()
.filter(n -> n % 2 == 0)
.filter(n -> n > 10)
.sorted()
.collect(Collectors.toList());
可以看到,Stream代码更简洁、更易读,更像是在描述问题本身而不是实现细节。
Stream的核心概念
1. 创建Stream
有多种方式可以创建Stream:
// 从集合创建List<String> list = Arrays.asList("a", "b", "c");Stream<String> stream = list.stream();// 从数组创建String[] array = {"a", "b", "c"};Stream<String> stream = Arrays.stream(array);// 直接创建Stream<String> stream = Stream.of("a", "b", "c");// 创建数字流IntStream intStream = IntStream.range(1, 5); // 1,2,3,4
2. 中间操作 vs 终止操作
Stream操作分为两类:
-
中间操作:返回Stream本身,可以链式调用(如filter, map, sorted)
-
终止操作:返回具体结果或执行操作(如collect, forEach, count)
List<String> result = list.stream() // 创建流
.filter(s -> s.startsWith("a")) // 中间操作
.map(String::toUpperCase) // 中间操作
.collect(Collectors.toList()); // 终止操作
3. 常用操作详解
filter - 过滤
// 筛选出长度大于3的字符串List<String> list = Arrays.asList("java", "py", "c", "javascript");List<String> result = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());// 结果: ["java", "javascript"]
map - 映射/转换
// 将字符串转换为大写List<String> list = Arrays.asList("java", "python");List<String> result = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());// 结果: ["JAVA", "PYTHON"]// 提取对象属性List<User> users = Arrays.asList(new User("Alice", 25), new User("Bob", 30));List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());// 结果: ["Alice", "Bob"]
sorted - 排序
// 自然排序List<String> list = Arrays.asList("java", "python", "c");List<String> result = list.stream()
.sorted()
.collect(Collectors.toList());// 结果: ["c", "java", "python"]// 自定义排序List<String> result = list.stream()
.sorted((s1, s2) -> s2.length() - s1.length()) // 按长度降序
.collect(Collectors.toList());// 结果: ["python", "java", "c"]
distinct - 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);List<Integer> result = numbers.stream()
.distinct()
.collect(Collectors.toList());// 结果: [1, 2, 3]
limit/skip - 限制/跳过
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);List<Integer> result = numbers.stream()
.skip(2) // 跳过前2个元素
.limit(5) // 只取5个元素
.collect(Collectors.toList());// 结果: [3, 4, 5, 6, 7]
4. 终止操作
collect - 收集结果
// 转换为ListList<String> list = stream.collect(Collectors.toList());// 转换为SetSet<String> set = stream.collect(Collectors.toSet());// 转换为MapMap<String, Integer> map = stream.collect(
Collectors.toMap(s -> s, String::length)); // key:元素本身, value:长度// 拼接字符串String result = stream.collect(Collectors.joining(", "));
forEach - 遍历
// 打印每个元素stream.forEach(System.out::println);
count - 计数
long count = stream.filter(s -> s.length() > 3).count();
reduce - 归约
// 求和List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);int sum = numbers.stream().reduce(0, Integer::sum);// 结果: 15// 找出最长字符串Optional<String> longest = strings.stream()
.reduce((s1, s2) -> s1.length() > s2.length() ? s1 : s2);
并行流:发挥多核优势
Stream的强大之处还在于可以轻松实现并行处理:
// 顺序流List<String> result = list.stream()...collect(Collectors.toList());// 并行流 - 自动利用多核处理器List<String> result = list.parallelStream()...collect(Collectors.toList());
只需要将
stream()改为
parallelStream(),Java会自动将任务分解到多个CPU核心上执行,大幅提升大数据集的处理速度。
实际应用示例
示例1:数据处理管道
// 处理用户数据:筛选活跃用户,提取用户名,排序,去重List<String> activeUserNames = users.stream()
.filter(User::isActive) // 筛选活跃用户
.map(User::getName) // 提取用户名
.sorted() // 按字母排序
.distinct() // 去重
.collect(Collectors.toList()); // 收集结果
示例2:统计计算
// 计算产品平均价格double averagePrice = products.stream()
.filter(p -> p.getCategory().equals("Electronics"))
.mapToDouble(Product::getPrice) // 转换为DoubleStream
.average() // 计算平均值
.orElse(0.0); // 如果无结果返回0.0
示例3:分组操作
// 按部门分组员工Map<String, List<Employee>> employeesByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));// 按城市分组并计算每个城市的平均工资Map<String, Double> avgSalaryByCity = employees.stream()
.collect(Collectors.groupingBy(
Employee::getCity,
Collectors.averagingDouble(Employee::getSalary)
));https://zq.zhaopin.com/moment/73399625https://zq.zhaopin.com/moment/73399625
https://zq-mobile.zhaopin.com/moment/73399625
https://zhiq.zhaopin.com/moment/73399625
https://zq.zhaopin.com/moment/73399619
https://zq-mobile.zhaopin.com/moment/73399619
https://zhiq.zhaopin.com/moment/73399619
https://zq.zhaopin.com/moment/73399615
https://zq-mobile.zhaopin.com/moment/73399615
https://zhiq.zhaopin.com/moment/73399615
https://zq.zhaopin.com/moment/73399612
https://zq-mobile.zhaopin.com/moment/73399612
https://zhiq.zhaopin.com/moment/73399612
https://zq.zhaopin.com/moment/73399604
https://zq-mobile.zhaopin.com/moment/73399604
https://zhiq.zhaopin.com/moment/73399604
https://zq.zhaopin.com/moment/73399599
https://zq-mobile.zhaopin.com/moment/73399599
https://zhiq.zhaopin.com/moment/73399599
https://zq.zhaopin.com/moment/73399593
使用建议
-
保持简洁:Stream链不宜过长,过长时应考虑拆分或重构
-
避免副作用:不要在lambda表达式中修改外部状态
-
优先使用方法引用:使代码更简洁(如
String::length代替
s -> s.length())
-
注意性能:小数据集可能传统循环更快,大数据集并行流更有优势
-
合理使用并行流:数据量小或操作简单时,并行可能更慢
总结
Stream是Java 8带来的革命性特性,它让集合操作变得更加:
-
声明式:关注"做什么"而非"怎么做"
-
可组合:操作可以像管道一样连接起来
-
可并行:轻松利用多核处理器提升性能
-
简洁优雅:代码更简洁易读
掌握Stream不仅能让你的代码更加现代化,还能提高开发效率和程序性能。从今天开始,尝试用Stream重构你的集合处理代码吧!