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

方法1:遍历两个集合:

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

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

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

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

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