(附:祖传for循环 vs Stream性能对决 + 并行流骚操作)
一、传统循环:代码界的“996福报”
场景:从100个商品里,挑出价格超过50块的,打八折后收集名字。
祖传for循环写法
List<String> result = new ArrayList<>();
for (Product product : productList) { // 这循环比老板的会议还长
if (product.getPrice() > 50) { // 条件判断
product.setPrice(product.getPrice() * 0.8); // 打折
result.add(product.getName()); // 收集名字
}
}
// 点评:这代码写的我仿佛在流水线上拧螺丝!
Stream工业革命版
List<String> result = productList.stream()
.filter(p -> p.getPrice() > 50) // 过滤:只留贵的
.peek(p -> p.setPrice(p.getPrice() * 0.8)) // 打折:偷偷改价格
.map(Product::getName) // 变形:只要名字
.collect(Collectors.toList()); // 打包:装车带走
// 点评:这代码像坐上了自动化流水线,直接起飞!
省流总结:
-
代码行数:7行 → 5行(看似差不多,但逼格翻倍)
-
可读性:流水线式操作,逻辑直男式清晰
-
逼格指数:从“搬砖工”晋升为“车间主任”
二、Stream核心操作:流水线上的“三大金刚”
1. filter(筛子)
// 筛出所有姓“张”的员工
List<Employee> zhangs = employees.stream()
.filter(e -> e.getName().startsWith("张"))
.collect(Collectors.toList());
// 点评:这筛子比大妈挑菜还严格!
2. map(变形器)
// 把员工列表变成工资列表
List<Double> salaries = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.toList());
// 点评:直接榨干员工,只留工资!
3. collect(打包机)
// 把商品按类别分组
Map<String, List<Product>> groupByCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
// 点评:分类打包,直接送去双十一仓库!
骚操作:
flatMap:把嵌套集合拍平(比如List<List<Integer>> → List<Integer>)sorted:排序(支持自定义Comparator,比老板的喜好还灵活)limit/skip:分页操作(比食堂阿姨手抖还精准)
三、并行流:榨干CPU的“黑心工厂”
单线程Stream
long count = productList.stream()
.filter(p -> p.getPrice() > 100)
.count();
并行流暴力版
long count = productList.parallelStream() // 加个parallel,直接开多线程
.filter(p -> p.getPrice() > 100)
.count();
性能真相(测试数据来自贴吧老哥的二手i7电脑):
| 数据量 | 单线程Stream耗时 | 并行流耗时 | 结论 |
|---|---|---|---|
| 1万 | 12ms | 25ms | 并行流启动反增耗时 |
| 100万 | 150ms | 80ms | 并行开始有优势 |
| 1000万 | 1450ms | 380ms | 并行流碾压 |
老哥忠告:
- 数据量小别用并行流,线程切换比干活还累!
- 线程池默认用
ForkJoinPool,小心和业务代码抢资源!
四、性能优化のののののののののののののののの玄学
1. 短路操作:用findFirst代替filter+limit
// 低效写法
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() > 100)
.limit(1)
.findFirst();
// 高效写法(找到第一个直接跑路)
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() > 100)
.findFirst();
2. 避免重复计算
// 错误示范:每次循环都计算
products.stream()
.map(p -> p.getPrice() * exchangeRate) // exchangeRate在循环外定义
.collect(Collectors.toList());
// 正确姿势:预先计算变量
double finalRate = exchangeRate;
products.stream()
.map(p -> p.getPrice() * finalRate)
.collect(Collectors.toList());
五、Stream作死行为大赏
1. 在Stream里修改外部变量
int sum = 0;
products.stream()
.forEach(p -> sum += p.getPrice()); // 编译报错!变量必须是final或等效final
正确姿势:用reduce求和!
2. 无限流作死
Stream.generate(() -> "加班!")
.forEach(System.out::println); // 无限打印,直到电脑爆炸
忠告:搭配limit(100)使用,保你狗命!
六、下期预告
《Optional类:从“连环判空”到“优雅防崩”,让你的代码告别NullPointerExceptionのののののの骚操作》
互动:评论区吐槽你最想用Stream暴改哪段祖传代码?点赞前三送学习资料!
(注:本文测试数据纯属贴吧老哥瞎编,实际效果可能因电脑配置、老板催促程度等因素波动)