带你读《2022技术人的百宝黑皮书》——stream的实用方法和注意事项(2)https://developer.aliyun.com/article/1339661?groupCode=taobaotech
原理不少人可能会觉得简单易懂,但遗憾的是在大型项目中往往总能找到有此类性能缺陷的代码,诸如
List<Long> awardId = timeFilterAwardConfigs.stream() .map(config -> config.getAwardId()) .filter(awardId -> awardId > 0) .collect(Collectors.toList());
但在更复杂的场景下,也并非要求filter无脑提前于其他操作。比如下面这个例子
//假设一份用户集 List<User> userList = Arrays.asList( new User("张三", 22) , new User("李四", 21) , new User("王五", 19) , new User("赵六", 25) ); //要输出这份集合中所有用户所就职的公司的年度营业额总和,要求公司所在地都在杭州市余杭区 // 注意用户中可能有无业游民。不考虑就职公司重合或者一人就职多家公司的情况。 //写法一 int allCompanyTurnover1 = userList.stream() .map(user -> calculateAnnualTurnover(queryUserCompany(user))) .filter(Objects::nonNull) .reduce(0, Integer::sum); //写法二 int allCompanyTurnover2 = userList.stream() .filter(user -> { Company company = queryUserCompany(user); return company != null && !"余杭".equals(company.getLocal()); }) .map(user -> calculateAnnualTurnover(queryUserCompany(user))) .reduce(0, Integer::sum);
写法一显然更符合直觉,写法二虽然filter提前过滤掉了一部分数据,但是queryUserCompany存在重复计算。所以此种情况下就需要综合 filter过滤度和queryUserCompany重复计算的开销进行权衡。如果filter过滤度足够高
(比如余杭的公司很少)同时queryUserCompany 资源开销不大,那么写法二更优,反之写法一更优。
并非适用所有场景
性能上
这里就可以说回到刚才讲anyMatch时看到的那段代码
//判断昨天是否签到过。写法一 boolean yesterdaySigned = calendars.stream() .anyMatch( t -> Days.daysBetween(t.getDate(), now).getDays() == 1 && t.isSigned() ); System.out.println("昨天是否签到过 -> " + yesterdaySigned); //写法二 boolean yesterdaySigned2 = false; for (Calendar calendar : calendars) { if (Days.daysBetween(calendar.getDate(), now).getDays() == 1) { //找到昨天的日历,并判断是否签到yesterdaySigned2 = calendar.isSigned(); break; } } System.out.println("昨天是否签到过写法二 -> " + yesterdaySigned2);
带你读《2022技术人的百宝黑皮书》——stream的实用方法和注意事项(4)https://developer.aliyun.com/article/1339659?groupCode=taobaotech