前情提要我们在开发中经常碰到这样的场景,查出两个 list 集合数据,需要根据他们相同的某个属性为连接点,进行聚合 。但是平时我们使用的时候关注过性能吗?下面让我们一起来看看它的表现如何 。
来个例子我们现在有两个 List集合 , 需要根据他们相同的 personId 进行聚合处理,我们很容易想到的写法是这样的:
private static void test1(List<Person> list1, List<Person> list2) {for (Person before:list1){for (Person after:list2){if(before.getPersonId().equals(after.getPersonId())){//TODO 业务逻辑break;}}}}
这样的代码是我们开发中最常用的一种方式,数据少的话没问题 。如果数据量大的会很慢,接下来我做一个实验 。看看在 1w 和 10w 的数据量下他的性能如何?
测试代码如下:
public static void main(String[] args) {List<Person> list1= new ArrayList<>();List<Person> list2= new ArrayList<>();for (int i = 0; i < 10_0000; i++) {list1.add(Person.builder().personId(Long.valueOf(i+"")).build());list2.add(Person.builder().personId(Long.valueOf(i+"")).build());}long start = System.currentTimeMillis();test1(list1, list2);System.out.println("for循环耗时:"+(System.currentTimeMillis()-start));
1w 耗时:343
10w 耗时:64285
文章插图
仅仅 10w 的数据竟然达到了 64 秒多,可以看出它的性能是多么差了吧 。
那怎么优化呢?我们可以把第二个 list 转为 map 的方式来做,示例如下:
代码如下:
private static void test2(List<Person> list1, List<Person> list2) {Map<Long, Person> baseMap =list2.stream().collect(Collectors.toMap(Person::getPersonId, Function.identity()));for (Person before:list1){Person after = baseMap.get(before.getPersonId());}}
接下来我们再进行下性能测试 。1w 耗时:88
10w 耗时:95
可以看出速度快了上百倍不止,如果还有小伙伴用第一种方式的话就赶紧优化了吧 。
思考我们想想第一种为什么会慢呢?
在第二个循环里他需要从 0 开始遍历所有的元素来进行比对,数据量越大,它需要遍历的数就越多,所以很慢 。所以如果我们业务上两个集合的大小和顺序一致(即能知道应该第二个循环能匹配上的元素在第几个),那么就能避免掉大量的循环 。
示例如下:
我们直接在第二层循环的时候 , 将下标先指定为和第一层循环的一致,如果他们俩属性相同,立马跳出;进行第二次循环 。
private static void test3(List<Person> list1, List<Person> list2) {for (int i=0;i<list1.size();i++){int jj = 0;for (int j = i; j < list2.size(); j++) {if (jj == list2.size()) {break;}if(list1.get(i).getPersonId().equals(list2.get(j).getPersonId())){// 编写具体的逻辑break;}if (j == list2.size() - 1) j = -1;jj += 1;}}}
性能测试如下:1w 耗时:2
【还在用双层for循环吗?太慢了】10w 耗时:13
我们发现又更加快了 。
下面是总体的测试数据:
数据量双层 for 循环循环+map改良版 for 循环100 条数据1 毫秒70 毫秒<1 毫秒1000 条数据16 毫秒91 毫秒1 毫秒5000 条数据66 毫秒66 毫秒3 毫秒1w 条数据208 毫秒64 毫秒4 毫秒10w 条数据62887 毫秒84 毫秒17 毫秒100w 条数据很久155 毫秒24毫秒总结:如果数据量小于 5000,推荐就用双层 for 循环,如果大于 5000 , 则使用循环+map 的方式 。
如果两个集合顺序一致 , 则可以用改良版的 for 循环
推荐阅读
- 苹果xr参数配置_苹果xr参数配置详细参数
- 微信好友删除了怎么找回(微信注销60天后警察还能查到吗)
- 玩游戏不流畅、卡帧、掉帧怎么办(打王者明明有60帧还是掉帧)
- 请问扑克牌名叫双人桥/蜜月桥的游戏怎么玩,还有规矩
- 小米路由器4c是5g还是2.4g的_小米路由器4c支持频段
- 抽烟怎么吐烟圈或者还能怎么提高B抽烟怎么
- 刚买的手机恢复出厂设置会怎样(手机恢复出厂设置还能恢复资料吗)
- Java:既然有了synchronized,为什么还要提供Lock?
- 枢密副使是个什么官 枢密副使
- 上古四大凶兽之一的梼杌有什么来历它还有别的名字吗