Java8 引入的 Stream API 是 Java 发展史上最重要的更新之一。它让集合操作从"怎么做"变成了"做什么",代码更简洁、更易读。
一、Stream 是什么
传统方式操作集合需要写大量 for 循环和 if 判断:
// 传统方式:筛选出年龄大于18的用户名List<String>names=newArrayList<>();for(Useruser:userList){if(user.getAge()>18){names.add(user.getName());}}用 Stream 一行搞定:
// Stream 方式List<String>names=userList.stream().filter(u->u.getAge()>18).map(User::getName).collect(Collectors.toList());二、创建 Stream
// 从集合创建List<String>list=Arrays.asList("a","b","c");Stream<String>stream1=list.stream();// 从数组创建String[]arr={"a","b","c"};Stream<String>stream2=Arrays.stream(arr);// 从值创建Stream<String>stream3=Stream.of("a","b","c");三、常用中间操作
中间操作返回的是一个新的 Stream,可以链式调用。
1. filter——筛选
// 筛选成绩大于等于60分的学生List<Student>passList=students.stream().filter(s->s.getScore()>=60).collect(Collectors.toList());2. map——转换
// 提取所有学生姓名List<String>names=students.stream().map(Student::getName).collect(Collectors.toList());// 对象转字符串List<String>infoList=students.stream().map(s->s.getName()+" - "+s.getScore()).collect(Collectors.toList());3. distinct——去重
// 获取所有不重复的班级List<String>classes=students.stream().map(Student::getClassName).distinct().collect(Collectors.toList());4. sorted——排序
// 按成绩降序排序List<Student>sorted=students.stream().sorted(Comparator.comparingInt(Student::getScore).reversed()).collect(Collectors.toList());// 先按班级、再按成绩排序List<Student>sorted2=students.stream().sorted(Comparator.comparing(Student::getClassName).thenComparingInt(Student::getScore)).collect(Collectors.toList());5. limit / skip——分页
// 跳过前10条,取10条(第二页)List<Student>page2=students.stream().skip(10).limit(10).collect(Collectors.toList());四、常用终止操作
终止操作才是真正执行计算的时候。
1. forEach——遍历
students.stream().filter(s->s.getScore()<60).forEach(s->System.out.println(s.getName()+"不及格"));2. count——计数
longcount=students.stream().filter(s->s.getScore()>=90).count();System.out.println("优秀学生人数: "+count);3. anyMatch / allMatch / noneMatch——匹配
// 是否有不及格的学生booleanhasFail=students.stream().anyMatch(s->s.getScore()<60);// 是否全部及格booleanallPass=students.stream().allMatch(s->s.getScore()>=60);// 是否没有不及格的booleannoFail=students.stream().noneMatch(s->s.getScore()<60);4. findFirst / findAny——查找
// 获取第一个成绩大于90的学生Optional<Student>top=students.stream().filter(s->s.getScore()>90).findFirst();top.ifPresent(s->System.out.println(s.getName()));五、collect——收集(最常用)
// 1. 转为 ListList<String>list=stream.collect(Collectors.toList());// 2. 转为 Set(自动去重)Set<String>set=stream.collect(Collectors.toSet());// 3. 转为 MapMap<String,Integer>map=students.stream().collect(Collectors.toMap(Student::getName,// keyStudent::getScore,// value(a,b)->a// key 冲突时保留第一个));// 4. 分组统计Map<String,List<Student>>group=students.stream().collect(Collectors.groupingBy(Student::getClassName));// 5. 分组后计数Map<String,Long>countByClass=students.stream().collect(Collectors.groupingBy(Student::getClassName,Collectors.counting()));// 6. 分组后求平均值Map<String,Double>avgByClass=students.stream().collect(Collectors.groupingBy(Student::getClassName,Collectors.averagingInt(Student::getScore)));// 7. 拼接字符串Stringnames=students.stream().map(Student::getName).collect(Collectors.joining(", "));// 输出: "张三, 李四, 王五"六、实战:学生成绩统计
@Data@AllArgsConstructorclassStudent{privateStringname;privateStringclassName;privateintscore;}publicclassStreamExample{publicstaticvoidmain(String[]args){List<Student>students=Arrays.asList(newStudent("张三","大数据2301",88),newStudent("李四","大数据2301",92),newStudent("王五","大数据2302",76),newStudent("赵六","大数据2302",45),newStudent("孙七","软件2301",63));// 1. 各班级平均分System.out.println("=== 各班平均分 ===");students.stream().collect(Collectors.groupingBy(Student::getClassName,Collectors.averagingInt(Student::getScore))).forEach((c,avg)->System.out.println(c+": "+avg));// 2. 不及格名单System.out.println("\n=== 不及格学生 ===");students.stream().filter(s->s.getScore()<60).forEach(s->System.out.println(s.getName()+": "+s.getScore()));// 3. 各班最高分System.out.println("\n=== 各班最高分 ===");students.stream().collect(Collectors.groupingBy(Student::getClassName,Collectors.maxBy(Comparator.comparingInt(Student::getScore)))).forEach((c,s)->System.out.println(c+": "+s.get().getName()+"("+s.get().getScore()+")"));// 4. 按成绩排名System.out.println("\n=== 成绩排名 ===");students.stream().sorted(Comparator.comparingInt(Student::getScore).reversed()).forEach(s->System.out.println(s.getName()+": "+s.getScore()));// 5. 成绩分布统计System.out.println("\n=== 成绩分布 ===");students.stream().collect(Collectors.groupingBy(s->{if(s.getScore()>=90)return"优秀";if(s.getScore()>=80)return"良好";if(s.getScore()>=70)return"中等";if(s.getScore()>=60)return"及格";return"不及格";},Collectors.counting())).forEach((level,count)->System.out.println(level+": "+count+"人"));}}七、并行流——利用多核提升性能
数据量大时,可以用parallelStream()自动并行处理:
// 并行流(数据量小的时候不要用,反而更慢)List<String>names=students.parallelStream().filter(s->s.getScore()>60).map(Student::getName).collect(Collectors.toList());| 数据量 | 普通流 | 并行流 |
|---|---|---|
| 100条 | 0.1ms | 0.3ms |
| 1万条 | 5ms | 3ms |
| 100万条 | 500ms | 80ms |
建议:数据量超过 10 万条再考虑并行流。
八、踩坑提醒
1. Stream 只能使用一次
Stream<String>stream=list.stream();stream.forEach(System.out::println);stream.filter(s->s.startsWith("A"));// 报错!stream 已关闭2. 不要在 forEach 中修改外部变量
// 错误List<String>result=newArrayList<>();list.stream().forEach(s->result.add(s));// 线程不安全// 正确List<String>result=list.stream().collect(Collectors.toList());3. Optional 判空
// 正确用法Optional<Student>opt=students.stream().filter(s->s.getScore()>90).findFirst();opt.ifPresent(s->System.out.println(s.getName()));// 不要直接用 get()Students=opt.get();// 没有值时会抛异常总结
Stream API 是现代 Java 开发的必备技能。掌握它之后,你会发现自己写 for 循环的次数越来越少。记住一个原则:
传统 for 循环 = 告诉计算机怎么做 → Stream = 告诉计算机要什么
如果对你有帮助,欢迎点赞、评论、关注【张老师技术栈】,持续分享 Java/Python/爬虫 实战干货。