java 两个list中的包含元素_Java Collection - 003 高效的找出两个List中的不同元素

如题:有List list1和List list2,两个集合各有上万个元素,怎样取出两个集合中不同的元素?

方法1:遍历两个集合

1 public static voidmain(String[] args) {2 List list1 = new ArrayList();3 List list2 = new ArrayList();4

5 for(int i = 0; i < 10000; i++){6 list1.add("test" +i);7 list2.add("test" + i*2);8 }9 getDifferent(list1, list2);10 getDiffrent2(list1, list2);11 getDiffrent3(list1, list2);12 getDiffrent4(list1, list2);13 }14

15 private static List getDifferent(List list1, Listlist2){16 long startTime =System.currentTimeMillis();17 List diff = new ArrayList();18 for(String str : list1){19 if(!list2.contains(str)){20 diff.add(str);21 }22 }23 System.out.println("Total Time: " + (System.currentTimeMillis() -startTime));24 returndiff;25 }

千万不要采用这种方法,总共要循环的次数是两个List的size相乘的积,从输出看耗时也是比较长的,那么我们有没有其他的方法呢?当然有.

方法2:采用List提供的retainAll()方法:

1 public static voidmain(String[] args) {2 List list1 = new ArrayList();3 List list2 = new ArrayList();4

5 for (int i = 0; i < 10000; i++) {6 list1.add("test" +i);7 list2.add("test" + i * 2);8 }9 getDifferent(list1, list2);10 }11

12 private static List getDiffrent2(List list1, Listlist2) {13 long startTime =System.currentTimeMillis();14 list1.retainAll(list2);15 System.out.println("Total Time: " + (System.currentTimeMillis() -startTime));16 returnlist1;17 }

很遗憾,这种方式虽然只要几行代码就搞定,但是这个却更耗时,查看retainAll()的源码:

1 public boolean retainAll(Collection>c) {2 boolean modified = false;3 Iterator e =iterator();4 while(e.hasNext()) {5 if (!c.contains(e.next())) {6 e.remove();7 modified = true;8 }9 }10 returnmodified;11 }

无需解释这个耗时是必然的,那么我们还有没有更好的办法呢?仔细分析以上两个方法中我都做了mXn次循环,其实完全没有必要循环这么多次,我们的需求是找出两个List中的不同元素,那么我可以这样考虑:用一个map存放lsit的所有元素,其中的key为lsit1的各个元素,value为该元素出现的次数,接着把list2的所有元素也放到map里,如果已经存在则value加1,最后我们只要取出map里value为1的元素即可,这样我们只需循环m+n次,大大减少了循环的次数。

1 private static List getDiffrent3(List list1, Listlist2) {2 long startTime =System.currentTimeMillis();3 Map map = new HashMap(list1.size() +list2.size());4 List diff = new ArrayList();5 for(String string : list1) {6 map.put(string, 1);7 }8 for(String string : list2) {9 Integer cc =map.get(string);10 if (cc != null) {11 map.put(string, ++cc);12 continue;13 }14 map.put(string, 1);15 }16 for (Map.Entryentry : map.entrySet()) {17 if (entry.getValue() == 1) {18 diff.add(entry.getKey());19 }20 }21 System.out.println("Total Time: " + (System.currentTimeMillis() -startTime));22 returnlist1;23 }

显然,这种方法大大减少耗时,是方法1的1/4,是方法2的1/40,这个性能的提升时相当可观的,但是,这不是最佳的解决方法,观察方法3我们只是随机取了一个list作为首次添加的标准,这样一旦我们的list2比list1的size大,则我们第二次put时的if判断也会耗时,做如下改进:

1 private static List getDiffrent4(List list1, Listlist2) {2 long st =System.nanoTime();3 Map map = new HashMap(list1.size()+list2.size());4 List diff = new ArrayList();5 List maxList =list1;6 List minList =list2;7 if(list2.size()>list1.size())8 {9 maxList =list2;10 minList =list1;11 }12 for(String string : maxList) {13 map.put(string, 1);14 }15 for(String string : minList) {16 Integer cc =map.get(string);17 if(cc!=null)18 {19 map.put(string, ++cc);20 continue;21 }22 map.put(string, 1);23 }24 for(Map.Entryentry:map.entrySet())25 {26 if(entry.getValue()==1)27 {28 diff.add(entry.getKey());29 }30 }31 System.out.println("getDiffrent4 total times "+(System.nanoTime()-st));32 returndiff;33

34 }

这里对连个list的大小进行了判断,小的在最后添加,这样会减少循环里的判断,性能又有了一定的提升,正如一位朋友所说,编程是无止境的,只要你认真去思考了,总会找到更好的方法!

非常感谢binglian的指正,针对List有重复元素的问题,做以下修正,首先明确一点,两个List不管有多少个重复,只要重复的元素在两个List都能找到,则不应该包含在返回值里面,所以在做第二次循环时,这样判断:如果当前元素在map中找不到,则肯定需要添加到返回值中,如果能找到则value++,遍历完之后diff里面已经包含了只在list2里而没在list2里的元素,剩下的工作就是找到list1里有list2里没有的元素,遍历map取value为1的即可:

1 private static List getDiffrent5(List list1, Listlist2) {2 long st =System.nanoTime();3 List diff = new ArrayList();4 List maxList =list1;5 List minList =list2;6 if(list2.size()>list1.size())7 {8 maxList =list2;9 minList =list1;10 }11 Map map = new HashMap(maxList.size());12 for(String string : maxList) {13 map.put(string, 1);14 }15 for(String string : minList) {16 if(map.get(string)!=null)17 {18 map.put(string, 2);19 continue;20 }21 diff.add(string);22 }23 for(Map.Entryentry:map.entrySet())24 {25 if(entry.getValue()==1)26 {27 diff.add(entry.getKey());28 }29 }30 System.out.println("getDiffrent5 total times "+(System.nanoTime()-st));31 returndiff;32

33 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值