大数组元素差异比较下的removeAll和Map效率对比
考虑这样一个场景,对两个列表对象,listA 和 listB,比较二者差异,找出只在 listA 中出现的元素列表 onlyListA,找出只在 listB 中出现的元素列表 onlyListB。removeAll实现
很容易想到借助 removeAll 实现,代码如下。List listA = new ArrayList<>(); List listB = new ArrayList<>(); //仅在数组A中出现的元素 List onlyListA = new ArrayList<>(listA); onlyListA.removeAll(listB); //仅在数组B中出现的元素 List onlyListB = new ArrayList<>(listB); onlyListB.removeAll(listA); 复制代码
当数组元素较少时,借助 removeAll 实现并没有任何问题。不过在数组元素较大时,removeAll 方法耗时会较大。执行如下测试方法,对数组元素个数为1000,1W,10W,100W 的场景进行测试。public class ListDiffTest { public static void main(String[] args) { testRemoveAllCostTime(1000); testRemoveAllCostTime(10000); testRemoveAllCostTime(100000); testRemoveAllCostTime(1000000); } public static void testRemoveAllCostTime(int size) { List listA = dataList(size); listA.add("onlyAElement"); List listB = dataList(size + 3); long startTime = System.currentTimeMillis(); //仅在数组A中出现的元素 List onlyListA = new ArrayList<>(listA); onlyListA.removeAll(listB); //仅在数组B中出现的元素 List onlyListB = new ArrayList<>(listB); onlyListB.removeAll(listA); System.out.println("仅在集合A中出现的元素:" + onlyListA); System.out.println("仅在集合B中出现的元素:" + onlyListB); System.out.println("元素个数 = " + size + "时,比对耗时:" + (System.currentTimeMillis() - startTime) + " 毫秒"); } private static List dataList(int size) { List dataList = new ArrayList<>(); for (int i = 0; i < size; i++) { dataList.add("" + i); } return dataList; } } 复制代码
测试结果如下仅在集合A中出现的元素:[onlyAElement] 仅在集合B中出现的元素:[1000, 1001, 1002] 元素个数 = 1000时,比对耗时:19 毫秒 元素个数 = 10000时,比对耗时:299 毫秒 #1W 元素个数 = 100000时,比对耗时:24848 毫秒 #10W 元素个数 = 1000000时,比对耗时:3607607 毫秒 #100W 约60m 复制代码
可以看到,当数组元素达到百万级时,耗时将达60min上下。借助Map实现
此处给出一种优化方式,借助 Map 计数,将 List 集合中的元素作为 Map 的 key,元素出现的次数作为 Map 的 value。代码实现如下。import io.vavr.Tuple2; public class ListDiffTest { public static void main(String[] args) { testDifferListByMapCostTime(1000); testDifferListByMapCostTime(10000); testDifferListByMapCostTime(100000); testDifferListByMapCostTime(1000000); } public static void testDifferListByMapCostTime(int size) { List listA = dataList(size); listA.add("onlyAElement"); List listB = dataList(size + 3); long startTime = System.currentTimeMillis(); //仅在数组A中出现的元素 List onlyListA = tuple2._1;; //仅在数组B中出现的元素 List onlyListB = tuple2._2; System.out.println("仅在集合A中出现的元素:" + onlyListA); System.out.println("仅在集合B中出现的元素:" + onlyListB); System.out.println("元素个数 = " + size + "时,比对耗时:" + (System.currentTimeMillis() - startTime) + " 毫秒"); } /** * 通过Map计数方式 比较两个数组之间的差异 * * @param listA 数组A * @param listB 数组B * @param 元素类型 * @return Tuple2对象 onlyAList-只在数组A存在的元素 onlyBList-只在数组B存在的元素 */ public static Tuple2, List> getDiffListBtMapCompare(List listA, List listB) { ValidateUtils.validateNotNull(listA, "listA"); ValidateUtils.validateNotNull(listB, "listB"); List onlyAList = new ArrayList<>(); List onlyBList = new ArrayList<>(); if (CollectionUtils.isEmpty(listA)) { return Tuple.of(onlyAList, listB); } else if (CollectionUtils.isEmpty(listB)) { return Tuple.of(listA, onlyBList); } /** * listA中元素 初始化计数 = 1 * listB中元素 初始化计数 = -2 * 遍历累加后 * 相同元素 计数 = 2 * 仅A中出现元素 计数 = 1 * 仅A中出现元素 计数 = -1 */ Map countMap = new HashMap<>(Math.max(listA.size(), listB.size())); for (E eleA : listA) { countMap.put(eleA, 1); } for (E eleB : listB) { countMap.put(eleB, 1 + countMap.getOrDefault(eleB, -2)); } countMap.forEach((k, v) -> { //获取不同元素集合 if (v == 1) { onlyAList.add(k); } else if (v == -1) { onlyBList.add(k); } }); return Tuple.of(onlyAList, onlyBList); } } 复制代码
测试结果如下仅在集合A中出现的元素:[onlyAElement] 仅在集合B中出现的元素:[1000, 1002, 1001] 元素个数 = 1000时,比对耗时:8 毫秒 元素个数 = 10000时,比对耗时:19 毫秒 #1W 元素个数 = 100000时,比对耗时:28 毫秒 #10W 元素个数 = 1000000时,比对耗时:96 毫秒 #100W 元素个数 = 10000000时,比对耗时:5320 毫秒 #1000W 复制代码removeAll耗时分析
最后,来分析下为什么在大数组元素比较时,removeAll 性能较差。removeAll 方法中,先进行判空,然后调用 batchRemove() 方法 public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); } 复制代码batchRemove() 方法中,使用 for 循环对集合进行遍历。第 1 层循环需要执行 listA.size() 次。循环体中调用了 contains() 方法来确定集合 B 是否含有该元素。 private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; } 复制代码contains() 方法的实现如下,内部又调用了 indexOf() 方法。indexOf() 方法内部又进行了一层 for 循环遍历。 public boolean contains(Object o) { return indexOf(o) >= 0; } public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; } 复制代码至此,可以看到,按照平均每次遍历要进行 list.size() / 2 次计算,假设集合 A 的元素个数为 m,集合 B 的元素个数为 n,则两重 for 循环下,会执行 m*n/2次。对于两个千万量级的数组,将执行 100 亿次计算!!!
由此给出一个结论,对于大数组元素差异比较,不建议使用 removeAll,可以借助 Map 实现。
原文链接:https://juejin.cn/post/7208099962911752247
惠民农商银行胡集支行开展农商送福活动大众网海报新闻记者段峰通讯员赵娜滨州报道迎新年贴春联是中华民族历年的传统习俗,为营造喜庆祥和的节日氛围,惠民农商银行胡集支行走进了各沿街主干道社区村庄企事业单位,开展了农商送福活动
侃财邦蚂蚁集团迎来第二春?新春将至,蚂蚁集团万余名员工,可以过一个好年了。这几天平台企业好消息接踵而至,先是重庆蚂蚁消费金融有限公司增资扩股终于落地,再是央行宣布蚂蚁集团等14家大型平台企业整改基本完成,紧
我们的东坡小小讲解员与苏轼的不解之缘视频加载中高山仰止回望东坡苏轼主题展正在四川博物院开展,展出了来自故宫博物院中国美术馆四川博物院等39家博物馆珍藏的274件苏轼主题文物。值得注意的是,本次主题展特别集合了一支32
网络中国节春节领着媳妇带着娃去肇源打出溜滑肇源冰雪节雪雕流光溢彩冰面上的童趣蒙古大营美轮美奂火树银花领着媳妇带着娃,去肇源打出溜滑。这是个极易挨揍的标题,打个出溜滑还得跑趟肇源?玩噱头也不带这么干的吧?如此辱人智商,怎不让
警察荣誉与中国式智慧高燃影视季现在,相较于观影,人们似乎更热衷于追剧。与电影相比,电视剧属于养成系,时间跨度大,更有利于表达人物。不过,养成也是一把双面镜,会更加考验编剧的厚度一些剧就败在了乏力的中后
中国一哥04惨败!丁俊晖最后1球,疑似主动认输,世界第一被淘汰中国一哥04惨败!丁俊晖最后1球,疑似主动认输,世界第一被淘汰。2023年斯诺克世界大奖赛18决赛,中国军团连输两场,周跃龙,丁俊晖先后出局,其中丁俊晖连丢4局,以04惨败给了马克
快报周薪4500英镑,C罗招聘私人主厨每日邮报克里斯蒂亚诺罗纳尔多(CristianoRonaldo)正在以每周4,500英镑的价格努力为他位于葡萄牙里维埃拉的价值1700万英镑的家聘请一名私人厨师,要求厨师的特色菜是
守护宝宝生命早期的肠道健康,益生菌一点都不能少守护宝宝生命早期的肠道健康益生菌一点都不能少几乎每个宝宝的成长路上都会经历便秘和腹泻儿童便秘或是腹泻往往都与肠道菌群的失衡相关肠道环境处于稳定状态时不同种类的菌群也能够和平相处但是
外媒近30年日本男性平均身越长越矮中国人身高越来越高1月18日,雅虎新闻网报道,日本文部科学省公开数据显示,1948年日本17岁男性平均身高为160。6厘米,1994年增至170。9厘米,2021年降为170。8厘米。日本国立成长发
回应转会传闻?扎尼奥洛转载C罗发言一切问题都有解决办法直播吧1月19日讯最近各大媒体都报道了扎尼奥洛可能在冬窗离开罗马的传闻,扎尼奥洛及其经纪人并没有出面对此做出回应。在社交媒体中,扎尼奥洛发布了一则动态,迪马济奥官网认为,这则动态是
同时摄入33。2亿个天体天文学家绘制银河系千里江山图一张每个人都清晰可辨的巨大合影。DECaPS2DOEFNALDECamCTIONOIRLabNSFAURA横亘夜空的银河如同一幅书画长卷,收录了不计其数的星辰。最近天文学家公布了一
1。年轻人创业最不能碰的行业我算是白手起家的典范,几乎做生意没投过什么钱,年轻人做生意最重要的是什么?第一,投资大。我平时最讨厌别人开奶茶店,奶茶店从我们数据上来看,倒闭了90以上,然后几乎没有成功的,动不动
指数有惊无险,我满仓做多受周末大漂亮的鹰派发言,早盘直接大幅低开后开始缓慢爬坡,最终收红盘结束,其实周末的时候我也是看到了很多空方的言论,说的天花乱坠的但是真正说明白的没几个,包括很多私募的基金经理也颇有
周二操作策略往后余生,我们一起爬最野的山喝最烈的酒撩最靓的妞打最硬的板一指数指数今天低开高走外围大跌给了早盘低开高走的机会这里指数维持震荡区间里运行不要期待大的调整!指数这里继续看区间震荡二市
家有男孩,想培养成有担当的男子汉,有15条黄金忠告请记牢闺蜜家老大是个男孩,一心想要个姑娘,于是狠下决心要了二胎,结果前段时间老二出生,又是个男孩,闺蜜欲哭无泪,还没出月子就得了产后抑郁,整天悲春伤秋,难过得不得了。不知从什么时候开始,
轲比能,一个鲜卑族首领,一顿操作把部落整得蒸蒸日上了轲比能,一个鲜卑族首领,一顿操作把部落整得蒸蒸日上了三国杀本期小叮当要给大家带来的是是一个具有超高配合性和兼容性的趣味武将,轲比能。这个人是鲜卑首领,学习汉族先进文化和技术,把他的
包揽5项大奖!这些格兰仕电器代表中国厨房新趋势8月26日,由中国家用电器协会指导中国家电网主办的2022中国厨电行业高峰论坛与美好家居沙龙在哈尔滨举行,共同探讨新时代厨房场景的新趋势与新机会。2022中国厨电行业高峰论坛上,格
华为PixLabV1发售近一月,鸿蒙3操作系统获用户超高评价7月27日华为发布了首款搭载HarmonyOS的彩色喷墨多功能打印机,仅仅一个月的时间就获得了超高的用户评价,好评如潮。从官网知乎等各大平台的用户留言可以看到,这款PixLabV1
徐开骋再出年度金句,这五段塌房明星的经典录音,听完太醒脑徐开骋上一个被全网群嘲的丁泽仁唯一的姐热度还没散去,这边徐开骋就接过了金句发言人的头衔,在互联网上为大家贡献了新的笑料。前女友张天爱公司怒怼徐开骋,称他是出轨惯犯,希望所有女孩擦亮
宋喆获刑6年将出狱?他们和王宝强的恩怨要结束了?当年王宝强妻子马蓉出轨事件也是大家有目共睹,老实人王宝强被出轨后也是非常决绝的直接在社交媒体上自爆妻子马蓉出轨丑闻,所谓家丑不可外扬,此等夺妻之恨,还是自己信赖有加的经济人,这是兄
迈克尔杰克逊唯一的女儿受尽宠爱,皮肤白到发光,身份曾被质疑Paris,帕里斯。杰克逊,大家都亲昵地叫她小巴黎。从1998年出生起,就成为备受关注的人,只因为,她是美国超级巨星迈克尔杰克逊唯一的女儿。当她在2020年爆出和男友加布里埃尔要进
张翰,都38岁了,还没长大呢?在无人在意的角落,由张翰自己当编剧当制片人当出品人组局拉人主演的东八区的先生们开播了。这是一部男性群像剧,被宣传为男版三十而已,中年版的欢乐颂。但看完播出的4集后,小8只想说张翰,