转自同名博文,未知真正出处,望作者见谅

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

方法1:遍历两个集合:

  1. package com.czp.test;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. public class TestList {
  5. public static void main(String[] args) {
  6. List<String> list1 = new ArrayList<String>();
  7. List<String> list2 = new ArrayList<String>();
  8. for (int i = 0; i < 10000; i++) {
  9. list1.add("test"+i);
  10. list2.add("test"+i*2);
  11. }
  12. getDiffrent(list1,list2);
  13. //输出:total times 2566454675
  14. }
  15. /**
  16. * 获取两个List的不同元素
  17. * @param list1
  18. * @param list2
  19. * @return
  20. */
  21. private static List<String> getDiffrent(List<String> list1, List<String> list2) {
  22. long st = System.nanoTime();
  23. List<String> diff = new ArrayList<String>();
  24. for(String str:list1)
  25. {
  26. if(!list2.contains(str))
  27. {
  28. diff.add(str);
  29. }
  30. }
  31. System.out.println("total times "+(System.nanoTime()-st));
  32. return diff;
  33. }
  34. }
千万不要采用这种方法,总共要循环的次数是两个List的size相乘的积,从输出看耗时也是比较长的,那么我们有没有其他的方法呢?当然有.

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

  1. package com.czp.test;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. public class TestList {
  5. public static void main(String[] args) {
  6. List<String> list1 = new ArrayList<String>();
  7. List<String> list2 = new ArrayList<String>();
  8. for (int i = 0; i < 10000; i++) {
  9. list1.add("test"+i);
  10. list2.add("test"+i*2);
  11. }
  12. getDiffrent(list1,list2);
  13. //输出:total times 2566454675
  14. getDiffrent2(list1,list2);
  15. //输出:getDiffrent2 total times 2787800964
  16. }
  17. /**
  18. * 获取连个List的不同元素
  19. * @param list1
  20. * @param list2
  21. * @return
  22. */
  23. private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
  24. long st = System.nanoTime();
  25. list1.retainAll(list2);
  26. System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
  27. return list1;
  28. }
  29. /**
  30. * 获取两个List的不同元素
  31. * @param list1
  32. * @param list2
  33. * @return
  34. */
  35. private static List<String> getDiffrent(List<String> list1, List<String> list2) {
  36. long st = System.nanoTime();
  37. List<String> diff = new ArrayList<String>();
  38. for(String str:list1)
  39. {
  40. if(!list2.contains(str))
  41. {
  42. diff.add(str);
  43. }
  44. }
  45. System.out.println("getDiffrent total times "+(System.nanoTime()-st));
  46. return diff;
  47. }
  48. }
  49. 很遗憾,这种方式虽然只要几行代码就搞定,但是这个却更耗时,查看retainAll()的源码:
  50. public boolean retainAll(Collection<?> c) {
  51. boolean modified = false;
  52. Iterator<E> e = iterator();
  53. while (e.hasNext()) {
  54. if (!c.contains(e.next())) {
  55. e.remove();
  56. modified = true;
  57. }
  58. }
  59. return modified;
  60. }
无需解释这个耗时是必然的,那么我们还有没有更好的办法呢?仔细分析以上两个方法中我都做了mXn次循环,其实完全没有必要循环这么多次,我们的需求是找出两个List中的不同元素,那么我可以这样考虑:用一个map存放lsit的所有元素,其中的key为lsit1的各个元素,value为该元素出现的次数,接着把list2的所有元素也放到map里,如果已经存在则value加1,最后我们只要取出map里value为1的元素即可,这样我们只需循环m+n次,大大减少了循环的次数。
  1. package com.czp.test;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. public class TestList {
  7. public static void main(String[] args) {
  8. List<String> list1 = new ArrayList<String>();
  9. List<String> list2 = new ArrayList<String>();
  10. for (int i = 0; i < 10000; i++) {
  11. list1.add("test"+i);
  12. list2.add("test"+i*2);
  13. }
  14. getDiffrent(list1,list2);
  15. //输出:total times 2566454675
  16. getDiffrent2(list1,list2);
  17. //输出:getDiffrent2 total times 2787800964
  18. getDiffrent3(list1,list2);
  19. //输出:getDiffrent3 total times 61763995
  20. }
  21. /**
  22. * 获取两个List的不同元素
  23. * @param list1
  24. * @param list2
  25. * @return
  26. */
  27. private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
  28. long st = System.nanoTime();
  29. Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
  30. List<String> diff = new ArrayList<String>();
  31. for (String string : list1) {
  32. map.put(string, 1);
  33. }
  34. for (String string : list2) {
  35. Integer cc = map.get(string);
  36. if(cc!=null)
  37. {
  38. map.put(string, ++cc);
  39. continue;
  40. }
  41. map.put(string, 1);
  42. }
  43. for(Map.Entry<String, Integer> entry:map.entrySet())
  44. {
  45. if(entry.getValue()==1)
  46. {
  47. diff.add(entry.getKey());
  48. }
  49. }
  50. System.out.println("getDiffrent3 total times "+(System.nanoTime()-st));
  51. return list1;
  52. }
  53. /**
  54. * 获取两个List的不同元素
  55. * @param list1
  56. * @param list2
  57. * @return
  58. */
  59. private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
  60. long st = System.nanoTime();
  61. list1.retainAll(list2);
  62. System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
  63. return list1;
  64. }
  65. /**
  66. * 获取两个List的不同元素
  67. * @param list1
  68. * @param list2
  69. * @return
  70. */
  71. private static List<String> getDiffrent(List<String> list1, List<String> list2) {
  72. long st = System.nanoTime();
  73. List<String> diff = new ArrayList<String>();
  74. for(String str:list1)
  75. {
  76. if(!list2.contains(str))
  77. {
  78. diff.add(str);
  79. }
  80. }
  81. System.out.println("getDiffrent total times "+(System.nanoTime()-st));
  82. return diff;
  83. }
  84. }
显然,这种方法大大减少耗时,是方法1的1/4,是方法2的1/40,这个性能的提升时相当可观的,但是,这不是最佳的解决方法,观察方法3我们只是随机取了一个list作为首次添加的标准,这样一旦我们的list2比list1的size大,则我们第二次put时的if判断也会耗时,做如下改进:
  1. package com.czp.test;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. public class TestList {
  7. public static void main(String[] args) {
  8. List<String> list1 = new ArrayList<String>();
  9. List<String> list2 = new ArrayList<String>();
  10. for (int i = 0; i < 10000; i++) {
  11. list1.add("test"+i);
  12. list2.add("test"+i*2);
  13. }
  14. getDiffrent(list1,list2);
  15. getDiffrent2(list1,list2);
  16. getDiffrent3(list1,list2);
  17. getDiffrent4(list1,list2);
  18. //        getDiffrent total times 2789492240
  19. //        getDiffrent2 total times 3324502695
  20. //        getDiffrent3 total times 24710682
  21. //        getDiffrent4 total times 15627685
  22. }
  23. /**
  24. * 获取两个List的不同元素
  25. * @param list1
  26. * @param list2
  27. * @return
  28. */
  29. private static List<String> getDiffrent4(List<String> list1, List<String> list2) {
  30. long st = System.nanoTime();
  31. Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
  32. List<String> diff = new ArrayList<String>();
  33. List<String> maxList = list1;
  34. List<String> minList = list2;
  35. if(list2.size()>list1.size())
  36. {
  37. maxList = list2;
  38. minList = list1;
  39. }
  40. for (String string : maxList) {
  41. map.put(string, 1);
  42. }
  43. for (String string : minList) {
  44. Integer cc = map.get(string);
  45. if(cc!=null)
  46. {
  47. map.put(string, ++cc);
  48. continue;
  49. }
  50. map.put(string, 1);
  51. }
  52. for(Map.Entry<String, Integer> entry:map.entrySet())
  53. {
  54. if(entry.getValue()==1)
  55. {
  56. diff.add(entry.getKey());
  57. }
  58. }
  59. System.out.println("getDiffrent4 total times "+(System.nanoTime()-st));
  60. return diff;
  61. }
  62. /**
  63. * 获取两个List的不同元素
  64. * @param list1
  65. * @param list2
  66. * @return
  67. */
  68. private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
  69. long st = System.nanoTime();
  70. Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
  71. List<String> diff = new ArrayList<String>();
  72. for (String string : list1) {
  73. map.put(string, 1);
  74. }
  75. for (String string : list2) {
  76. Integer cc = map.get(string);
  77. if(cc!=null)
  78. {
  79. map.put(string, ++cc);
  80. continue;
  81. }
  82. map.put(string, 1);
  83. }
  84. for(Map.Entry<String, Integer> entry:map.entrySet())
  85. {
  86. if(entry.getValue()==1)
  87. {
  88. diff.add(entry.getKey());
  89. }
  90. }
  91. System.out.println("getDiffrent3 total times "+(System.nanoTime()-st));
  92. return diff;
  93. }
  94. /**
  95. * 获取连个List的不同元素
  96. * @param list1
  97. * @param list2
  98. * @return
  99. */
  100. private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
  101. long st = System.nanoTime();
  102. list1.retainAll(list2);
  103. System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
  104. return list1;
  105. }
  106. /**
  107. * 获取两个List的不同元素
  108. * @param list1
  109. * @param list2
  110. * @return
  111. */
  112. private static List<String> getDiffrent(List<String> list1, List<String> list2) {
  113. long st = System.nanoTime();
  114. List<String> diff = new ArrayList<String>();
  115. for(String str:list1)
  116. {
  117. if(!list2.contains(str))
  118. {
  119. diff.add(str);
  120. }
  121. }
  122. System.out.println("getDiffrent total times "+(System.nanoTime()-st));
  123. return diff;
  124. }
  125. }
这里对连个list的大小进行了判断,小的在最后添加,这样会减少循环里的判断,性能又有了一定的提升,正如一位朋友所说,编程是无止境的,只要你认真去思考了,总会找到更好的方法!

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

  1. package com.czp.test;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. public class TestList {
  7. public static void main(String[] args) {
  8. List<String> list1 = new ArrayList<String>();
  9. List<String> list2 = new ArrayList<String>();
  10. for (int i = 0; i < 10000; i++) {
  11. list1.add("test"+i);
  12. list2.add("test"+i*2);
  13. }
  14. getDiffrent(list1,list2);
  15. getDiffrent3(list1,list2);
  16. getDiffrent5(list1,list2);
  17. getDiffrent4(list1,list2);
  18. getDiffrent2(list1,list2);
  19. //        getDiffrent3 total times 32271699
  20. //        getDiffrent5 total times 12239545
  21. //        getDiffrent4 total times 16786491
  22. //        getDiffrent2 total times 2438731459
  23. }
  24. /**
  25. * 获取两个List的不同元素
  26. * @param list1
  27. * @param list2
  28. * @return
  29. */
  30. private static List<String> getDiffrent5(List<String> list1, List<String> list2) {
  31. long st = System.nanoTime();
  32. List<String> diff = new ArrayList<String>();
  33. List<String> maxList = list1;
  34. List<String> minList = list2;
  35. if(list2.size()>list1.size())
  36. {
  37. maxList = list2;
  38. minList = list1;
  39. }
  40. Map<String,Integer> map = new HashMap<String,Integer>(maxList.size());
  41. for (String string : maxList) {
  42. map.put(string, 1);
  43. }
  44. for (String string : minList) {
  45. if(map.get(string)!=null)
  46. {
  47. map.put(string, 2);
  48. continue;
  49. }
  50. diff.add(string);
  51. }
  52. for(Map.Entry<String, Integer> entry:map.entrySet())
  53. {
  54. if(entry.getValue()==1)
  55. {
  56. diff.add(entry.getKey());
  57. }
  58. }
  59. System.out.println("getDiffrent5 total times "+(System.nanoTime()-st));
  60. return diff;
  61. }
  62. /**
  63. * 获取两个List的不同元素
  64. * @param list1
  65. * @param list2
  66. * @return
  67. */
  68. private static List<String> getDiffrent4(List<String> list1, List<String> list2) {
  69. long st = System.nanoTime();
  70. Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
  71. List<String> diff = new ArrayList<String>();
  72. List<String> maxList = list1;
  73. List<String> minList = list2;
  74. if(list2.size()>list1.size())
  75. {
  76. maxList = list2;
  77. minList = list1;
  78. }
  79. for (String string : maxList) {
  80. map.put(string, 1);
  81. }
  82. for (String string : minList) {
  83. Integer cc = map.get(string);
  84. if(cc!=null)
  85. {
  86. map.put(string, ++cc);
  87. continue;
  88. }
  89. map.put(string, 1);
  90. }
  91. for(Map.Entry<String, Integer> entry:map.entrySet())
  92. {
  93. if(entry.getValue()==1)
  94. {
  95. diff.add(entry.getKey());
  96. }
  97. }
  98. System.out.println("getDiffrent4 total times "+(System.nanoTime()-st));
  99. return diff;
  100. }
  101. /**
  102. * 获取两个List的不同元素
  103. * @param list1
  104. * @param list2
  105. * @return
  106. */
  107. private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
  108. long st = System.nanoTime();
  109. Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
  110. List<String> diff = new ArrayList<String>();
  111. for (String string : list1) {
  112. map.put(string, 1);
  113. }
  114. for (String string : list2) {
  115. Integer cc = map.get(string);
  116. if(cc!=null)
  117. {
  118. map.put(string, ++cc);
  119. continue;
  120. }
  121. map.put(string, 1);
  122. }
  123. for(Map.Entry<String, Integer> entry:map.entrySet())
  124. {
  125. if(entry.getValue()==1)
  126. {
  127. diff.add(entry.getKey());
  128. }
  129. }
  130. System.out.println("getDiffrent3 total times "+(System.nanoTime()-st));
  131. return diff;
  132. }
  133. /**
  134. * 获取连个List的不同元素
  135. * @param list1
  136. * @param list2
  137. * @return
  138. */
  139. private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
  140. long st = System.nanoTime();
  141. list1.retainAll(list2);
  142. System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
  143. return list1;
  144. }
  145. /**
  146. * 获取两个List的不同元素
  147. * @param list1
  148. * @param list2
  149. * @return
  150. */
  151. private static List<String> getDiffrent(List<String> list1, List<String> list2) {
  152. long st = System.nanoTime();
  153. List<String> diff = new ArrayList<String>();
  154. for(String str:list1)
  155. {
  156. if(!list2.contains(str))
  157. {
  158. diff.add(str);
  159. }
  160. }
  161. System.out.println("getDiffrent total times "+(System.nanoTime()-st));
  162. return diff;
  163. }
  164. }

两个list取不同值的更多相关文章

  1. vue中过滤器比较两个数组取相同值

    在vue中需要比较两个数组取相同值 一个大数组一个 小数组,小数组是大数组的一部分取相同ID的不同name值 有两种写法,两个for循环和map写法 const toName = (ids, arr) ...

  2. jsp取addFlashAttribute值深入理解即springMVC发redirect传隐藏参数

    结论:两种方式 a.如果没有进行action转发,在页面中el需要${sessionScope['org.springframework.web.servlet.support.SessionFlas ...

  3. hdu 5265 技巧题 O(nlogn)求n个数中两数相加取模的最大值

    pog loves szh II Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  4. UVA 10859 - Placing Lampposts 树形DP、取双优值

                              Placing Lampposts As a part of the mission ‘Beautification of Dhaka City’, ...

  5. go 两个数组取并集

    实际生产中,对不同数组取交集.并集.差集等场景很常用,下面来说下两个数组取差集 直接上代码: //两个集合取并集 package main import "fmt" //思想: / ...

  6. 定时ping取返回值并绘图

    figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...

  7. php取默认值以及类的继承

    (1)对于php的默认值的使用和C++有点类似,都是在函数的输入中填写默认值,以下是php方法中对于默认值的应用: <?phpfunction makecoffee($types = array ...

  8. java中两个Integer类型的值相比较的问题

    今天在做一个算法时,由于为了和其他人保持接口的数据类型一致,就把之前的int换为Integer,前几天测了几组数据,和之前的结果一样,但是今天在测其它数据 的时候,突然出现了一个奇怪的bug,由于之前 ...

  9. 在android的spinner中,实现取VALUE值和TEXT值。 ZT

    在android的spinner中,实现取VALUE值和TEXT值.   为了实现在android的 spinner实现取VALUE值和TEXT值,我尝试过好些办法,在网上查的资料,都是说修改适配器, ...

随机推荐

  1. ros语音交互(四)移植科大讯飞语音识别到ros

    将以前下载的的语音包的 samples/iat_record/的iat_record.c speech_recognizer.c speech_recognizer.c 拷贝到工程src中, linu ...

  2. ROS语音交互(三)科大讯飞语音在ROS平台下使用

    以上节tts语音输出为例 下载sdk链接:http://www.xfyun.cn/sdk/dispatcher 1.下载SDK,解压: 2.在ROS工作空间下创建一个Package: catkin_c ...

  3. ICEM(1)—边界结构网格绘制

    以两个圆为例 1. geometry→ create curve→ 选择圆,随便画两个圆 2. block下选择create block,选择第一项,initial block,设置改为2D Plan ...

  4. React Native 组件样式测试

    View组件默认样式(注意默认flexDirection:'column') {flexGrow:0,flexShrink:0,flexBasis:'auto',flexDirection:'colu ...

  5. c# 字符串操作

    一.字符串操作 //字符串转数组 string mystring="this is a string" char[] mychars=mystring.ToCharArray(); ...

  6. Emacs 相关资料翻译

    Table of Contents 1. 37 Document Viewing 2. EmacsrelatedTranslation 2.1. Spacemacs 配置层(Configuration ...

  7. Dynamic Programming

    We began our study of algorithmic techniques with greedy algorithms, which in some sense form the mo ...

  8. 横向滑动的GridView

    思路: GridView行数设置为一行,外面套一个HorizontalScrollView,代码中设置GridView宽度 xml代码 <HorizontalScrollView android ...

  9. Java第六次作业修改版

    import java.util.ArrayList; import java.util.Collections; import java.util.Random; public class Draw ...

  10. 细说JAVA反射

    Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性.例如,使用它能获得 Java 类中各成员的名称并显 ...