两个list取不同值

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

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

方法1:遍历两个集合:

[java] view plain copy

print?

  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()方法:

[java] view plain copy

print?

  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次,大大减少了循环的次数。

[java] view plain copy

print?

  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判断也会耗时,做如下改进:

[java] view plain copy

print?

  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的即可:

[java] view plain copy

print?

  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. }
时间: 2024-10-05 14:14:22

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

Linq使用Join/两次反射取属性值

var JoinList = _context .Cashes .Join(_context.MemberLogins, c => c.UserID, m => m.Id, (c, m) => new { c, m }) .Select(o => o) .DefaultIfEmpty() .ToList(); @if (Model.CheckList.Any()) { foreach (var item in Model.CheckList) { var Cash = item.G

九度OJ 1097 取中值

题目1097:取中值 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4379 解决:1188 题目描述: 存在两组数组,和4个数字a,b,c,d,要求做如下操作,将第一个数组第a个数到第b个数,第二个数组的第c个数到第d个数放到一个数组中,求出合并后数组的中间值,如果有两个中间值,取下标较小的那个. 输入: 第一行一个整数t表示有t个测试数据 第二行两个整数,表示两个数组的长度, 接下来两行表示两个数字的值, 最后一行有四个整数a,b,c,d. 数组长度不会超过1000000. 输出

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

                          Placing Lampposts As a part of the mission ‘Beauti?cation of Dhaka City’, the government has decided to replace all theold lampposts with new expensive ones. Since the new ones are quite expensive and the budget is notup to

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

pog loves szh II Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2106    Accepted Submission(s): 606 Problem Description Pog and Szh are playing games.There is a sequence with n numbers, Pog wi

两个数组取交集的算法

在Lucene中,如果用户的查询向量Term t = {xx,xx,……},BooleanQuery为AND时,每个t---->对应得到的倒排列表,倒排列表由许多的倒排索引项构成,然后取其中重复的文档编号,然后进行排序.器核心思想类似于如下问题: 现有两个数组:int []data1 = {12,45,65,2,5} int []data2 = {12,5,-8,9},取其中的交集. 实现的方案有很多,现在采取一种时间和空间相对较好的算法:集合压缩算法,即:1.分别计算两个数组的min和max(

从有序集合随机取一个值,应该用什么方案?

今天做了一个小实验,起因如下: 先在redis里构造了测试数据,如下: > zadd my_zset_999 1 35570 (integer) 1 > zadd my_zset_999 2 40617 (integer) 1 > zadd my_zset_999 3 40956 (integer) 1 > zadd my_zset_999 4 41151 (integer) 1 > > zrange my_zset_999 0 -1 WITHSCORES 1) &qu

不借助任何中间变量将两个整形变量的值交换

今天在做题的时候,突然出现一道题,就是不借助中间变量,将两个整形变量的值互换,开始有点懵,这怎么换?后来还是用两个变量不停做加减变换,差不多有十几分钟,才终于凑出来了一种方法,一时兴起,我又从网上找了一下相关的资料,说是竟然有四种方法,我就看了一下,顺便学习一下. 在我们初学阶段,一般是定义一个新的变量,借助它完成交换. 例如:int a,b;a=1; b=2;int t;t=a; a=b; b=t;这种算法易于理解,对初学者来说,一般都是用“空瓶子来回倒换”帮助理解的,而且是赋值语句的经典应用

jquery取选中值

多选 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Conte

在android的spinner中,实现取VALUE值和TEXT值

为了实现在android的spinner实现取VALUE值和TEXT值,我尝试过好些办法,在网上查的资料,都是说修改适配器,刚开始我也是通过修改适配器的方法来做的,但是如果一个activity有多个spinner,就需要多声明几个适配器的全局变量.后面查看适配器的源码发现,其实没有必要修改适配器,直接往适配器里面传对象,然后重写对象的toString()方法就可以简单解决.闲话少说,贴代码: 先声明一个对象,并重写对象的toString(). public class CItem {     p