如题:有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. }