Stream API实战用10个例子告别手写for循环开篇你还在写这种代码吗工作中处理集合数据时很多人习惯写一大段for循环先定义临时集合再if判断再add进去。代码又长又难读还容易出bug。JDK8引入的Stream API就是用来解决这个问题的。本文用10个真实开发中最常见的例子带你彻底掌握Stream API。一、Stream是什么Stream不是集合也不是数据结构。它是一套对集合数据进行函数式操作的工具。你可以把它理解为一条流水线。原料是集合经过过滤、映射、排序等工序最终得到想要的结果。ListIntegernumbersArrays.asList(1,2,3,4,5,6);ListIntegerevennumbers.stream().filter(n-n%20)// 过滤偶数.collect(Collectors.toList());// 收集成新集合【面试高频】Stream不会修改原集合而是返回一个新的结果。二、创建Stream的5种方式// 1. 从集合创建ListStringlistArrays.asList(a,b,c);StreamStringstream1list.stream();// 2. 从数组创建StreamIntegerstream2Arrays.stream(newInteger[]{1,2,3});// 3. 使用Stream.ofStreamStringstream3Stream.of(a,b,c);// 4. 创建无限流需要配合limitStreamIntegerstream4Stream.iterate(0,n-n2).limit(10);// 5. 创建随机流StreamDoublestream5Stream.generate(Math::random).limit(5);三、10个开发实战例子例子1过滤数据需求从用户列表中筛选出年龄大于18岁的用户。ListUseradultsusers.stream().filter(user-user.getAge()18).collect(Collectors.toList());对应手写代码需要五六行Stream一行搞定。例子2提取字段需求获取所有用户的姓名列表。ListStringnamesusers.stream().map(User::getName).collect(Collectors.toList());map的作用是把一个对象转换成另一个对象。这里是把User对象映射成String姓名。例子3排序需求按年龄从小到大排序。ListUsersortedusers.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());倒序也很简单.sorted(Comparator.comparing(User::getAge).reversed())例子4去重需求去除重复的姓名。ListStringdistinctNamesusers.stream().map(User::getName).distinct().collect(Collectors.toList());distinct()根据equals方法去重。如果是对象去重记得重写equals和hashCode。例子5聚合统计需求计算用户总年龄、平均年龄、最大年龄。IntSummaryStatisticsstatsusers.stream().collect(Collectors.summarizingInt(User::getAge));System.out.println(总数stats.getSum());System.out.println(平均stats.getAverage());System.out.println(最大stats.getMax());【面试高频】summarizingInt可以一次性得到总和、平均值、最大值、最小值和数量。例子6分组需求按性别分组。MapString,ListUsergroupByGenderusers.stream().collect(Collectors.groupingBy(User::getGender));返回的是一个Mapkey是性别value是对应用户列表。例子7字符串拼接需求把所有用户名用逗号连接成一个字符串。StringnameStrusers.stream().map(User::getName).collect(Collectors.joining(, ));结果类似张三, 李四, 王五。例子8分页需求取第2页数据每页10条。ListUserpageusers.stream().skip(10)// 跳过前10条.limit(10)// 取10条.collect(Collectors.toList());注意Stream分页适合内存中小数据量。大数据量分页还是在数据库层做。例子9匹配判断需求判断是否存在未成年用户是否所有用户都已成年。booleanhasMinorusers.stream().anyMatch(user-user.getAge()18);booleanallAdultusers.stream().allMatch(user-user.getAge()18);anyMatch是存在一个满足即可allMatch是全部都要满足。例子10归约求和需求计算所有用户年龄之和。inttotalAgeusers.stream().mapToInt(User::getAge).sum();或者使用reduceinttotalAgeusers.stream().map(User::getAge).reduce(0,Integer::sum);四、Stream vs for循环对比场景for循环Stream代码量多少可读性一般高是否修改原集合容易误改不修改原集合并行能力需要手写parallelStream()一键并行调试难度低较高【面试陷阱】Stream不是什么时候都比for循环好。简单循环、需要提前break、对性能要求极高的场景用for循环更合适。五、与框架的结合5.1 MyBatis查询结果处理// 查询出所有订单ListOrderordersorderMapper.selectAll();// 提取所有订单IDListLongorderIdsorders.stream().map(Order::getId).collect(Collectors.toList());5.2 Spring中对象转换ListUserVOvoListusers.stream().map(user-{UserVOvonewUserVO();BeanUtils.copyProperties(user,vo);returnvo;}).collect(Collectors.toList());六、开发踩坑实录坑 1在Stream中修改外部变量intsum0;numbers.stream().forEach(n-sumn);// 编译报错原因Lambda表达式中引用的外部变量必须是事实最终变量。正确做法intsumnumbers.stream().mapToInt(Integer::intValue).sum();坑 2Stream只能消费一次StreamIntegerstreamnumbers.stream();stream.filter(n-n3).collect(Collectors.toList());stream.filter(n-n3).collect(Collectors.toList());// 报错原因Stream是一次性流操作完就关闭了。正确做法需要重新创建Stream。坑 3parallelStream乱用list.parallelStream().forEach(this::writeToDatabase);后果多线程同时写数据库连接数暴涨还可能数据错乱。正确做法涉及IO、状态修改时谨慎使用并行流。坑 4在filter里写复杂逻辑users.stream().filter(user-{// 这里写了几十行业务逻辑return...;}).collect(Collectors.toList());后果代码可读性差难以维护。正确做法把复杂判断抽成方法保持Stream链简洁。七、面试速记卡7.1 常用操作速查操作作用示例filter过滤.filter(n - n 10)map映射.map(User::getName)sorted排序.sorted(Comparator.comparing(User::getAge))distinct去重.distinct()collect收集.collect(Collectors.toList())groupBy分组.collect(Collectors.groupingBy(User::getGender))joining拼接.collect(Collectors.joining(,))reduce归约.reduce(0, Integer::sum)anyMatch任意匹配.anyMatch(u - u.getAge() 18)allMatch全部匹配.allMatch(u - u.getAge() 18)7.2 高频面试题Stream和Collection有什么区别Stream的中间操作和终结操作有什么区别map和flatMap有什么区别Stream是惰性的吗parallelStream的原理是什么有什么风险为什么Stream只能使用一次八、小结Stream API让集合处理变得简洁优雅。记住三个核心步骤创建Stream → 中间操作 → 终结操作。日常开发中filter、map、collect、sorted、groupingBy这五个方法就能覆盖80%的场景。不要滥用Stream简单场景、性能敏感场景还是用for循环更稳妥。
13-Stream API实战:用10个例子告别手写for循环
发布时间:2026/7/15 21:26:08
Stream API实战用10个例子告别手写for循环开篇你还在写这种代码吗工作中处理集合数据时很多人习惯写一大段for循环先定义临时集合再if判断再add进去。代码又长又难读还容易出bug。JDK8引入的Stream API就是用来解决这个问题的。本文用10个真实开发中最常见的例子带你彻底掌握Stream API。一、Stream是什么Stream不是集合也不是数据结构。它是一套对集合数据进行函数式操作的工具。你可以把它理解为一条流水线。原料是集合经过过滤、映射、排序等工序最终得到想要的结果。ListIntegernumbersArrays.asList(1,2,3,4,5,6);ListIntegerevennumbers.stream().filter(n-n%20)// 过滤偶数.collect(Collectors.toList());// 收集成新集合【面试高频】Stream不会修改原集合而是返回一个新的结果。二、创建Stream的5种方式// 1. 从集合创建ListStringlistArrays.asList(a,b,c);StreamStringstream1list.stream();// 2. 从数组创建StreamIntegerstream2Arrays.stream(newInteger[]{1,2,3});// 3. 使用Stream.ofStreamStringstream3Stream.of(a,b,c);// 4. 创建无限流需要配合limitStreamIntegerstream4Stream.iterate(0,n-n2).limit(10);// 5. 创建随机流StreamDoublestream5Stream.generate(Math::random).limit(5);三、10个开发实战例子例子1过滤数据需求从用户列表中筛选出年龄大于18岁的用户。ListUseradultsusers.stream().filter(user-user.getAge()18).collect(Collectors.toList());对应手写代码需要五六行Stream一行搞定。例子2提取字段需求获取所有用户的姓名列表。ListStringnamesusers.stream().map(User::getName).collect(Collectors.toList());map的作用是把一个对象转换成另一个对象。这里是把User对象映射成String姓名。例子3排序需求按年龄从小到大排序。ListUsersortedusers.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());倒序也很简单.sorted(Comparator.comparing(User::getAge).reversed())例子4去重需求去除重复的姓名。ListStringdistinctNamesusers.stream().map(User::getName).distinct().collect(Collectors.toList());distinct()根据equals方法去重。如果是对象去重记得重写equals和hashCode。例子5聚合统计需求计算用户总年龄、平均年龄、最大年龄。IntSummaryStatisticsstatsusers.stream().collect(Collectors.summarizingInt(User::getAge));System.out.println(总数stats.getSum());System.out.println(平均stats.getAverage());System.out.println(最大stats.getMax());【面试高频】summarizingInt可以一次性得到总和、平均值、最大值、最小值和数量。例子6分组需求按性别分组。MapString,ListUsergroupByGenderusers.stream().collect(Collectors.groupingBy(User::getGender));返回的是一个Mapkey是性别value是对应用户列表。例子7字符串拼接需求把所有用户名用逗号连接成一个字符串。StringnameStrusers.stream().map(User::getName).collect(Collectors.joining(, ));结果类似张三, 李四, 王五。例子8分页需求取第2页数据每页10条。ListUserpageusers.stream().skip(10)// 跳过前10条.limit(10)// 取10条.collect(Collectors.toList());注意Stream分页适合内存中小数据量。大数据量分页还是在数据库层做。例子9匹配判断需求判断是否存在未成年用户是否所有用户都已成年。booleanhasMinorusers.stream().anyMatch(user-user.getAge()18);booleanallAdultusers.stream().allMatch(user-user.getAge()18);anyMatch是存在一个满足即可allMatch是全部都要满足。例子10归约求和需求计算所有用户年龄之和。inttotalAgeusers.stream().mapToInt(User::getAge).sum();或者使用reduceinttotalAgeusers.stream().map(User::getAge).reduce(0,Integer::sum);四、Stream vs for循环对比场景for循环Stream代码量多少可读性一般高是否修改原集合容易误改不修改原集合并行能力需要手写parallelStream()一键并行调试难度低较高【面试陷阱】Stream不是什么时候都比for循环好。简单循环、需要提前break、对性能要求极高的场景用for循环更合适。五、与框架的结合5.1 MyBatis查询结果处理// 查询出所有订单ListOrderordersorderMapper.selectAll();// 提取所有订单IDListLongorderIdsorders.stream().map(Order::getId).collect(Collectors.toList());5.2 Spring中对象转换ListUserVOvoListusers.stream().map(user-{UserVOvonewUserVO();BeanUtils.copyProperties(user,vo);returnvo;}).collect(Collectors.toList());六、开发踩坑实录坑 1在Stream中修改外部变量intsum0;numbers.stream().forEach(n-sumn);// 编译报错原因Lambda表达式中引用的外部变量必须是事实最终变量。正确做法intsumnumbers.stream().mapToInt(Integer::intValue).sum();坑 2Stream只能消费一次StreamIntegerstreamnumbers.stream();stream.filter(n-n3).collect(Collectors.toList());stream.filter(n-n3).collect(Collectors.toList());// 报错原因Stream是一次性流操作完就关闭了。正确做法需要重新创建Stream。坑 3parallelStream乱用list.parallelStream().forEach(this::writeToDatabase);后果多线程同时写数据库连接数暴涨还可能数据错乱。正确做法涉及IO、状态修改时谨慎使用并行流。坑 4在filter里写复杂逻辑users.stream().filter(user-{// 这里写了几十行业务逻辑return...;}).collect(Collectors.toList());后果代码可读性差难以维护。正确做法把复杂判断抽成方法保持Stream链简洁。七、面试速记卡7.1 常用操作速查操作作用示例filter过滤.filter(n - n 10)map映射.map(User::getName)sorted排序.sorted(Comparator.comparing(User::getAge))distinct去重.distinct()collect收集.collect(Collectors.toList())groupBy分组.collect(Collectors.groupingBy(User::getGender))joining拼接.collect(Collectors.joining(,))reduce归约.reduce(0, Integer::sum)anyMatch任意匹配.anyMatch(u - u.getAge() 18)allMatch全部匹配.allMatch(u - u.getAge() 18)7.2 高频面试题Stream和Collection有什么区别Stream的中间操作和终结操作有什么区别map和flatMap有什么区别Stream是惰性的吗parallelStream的原理是什么有什么风险为什么Stream只能使用一次八、小结Stream API让集合处理变得简洁优雅。记住三个核心步骤创建Stream → 中间操作 → 终结操作。日常开发中filter、map、collect、sorted、groupingBy这五个方法就能覆盖80%的场景。不要滥用Stream简单场景、性能敏感场景还是用for循环更稳妥。