zoukankan      html  css  js  c++  java
  • Java Collection

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

    方法1:遍历两个集合

     1 public static void main(String[] args) {
     2         List<String> list1 = new ArrayList<String>();
     3         List<String> list2 = new ArrayList<String>();
     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<String> getDifferent(List<String> list1, List<String> list2){
    16         long startTime = System.currentTimeMillis();
    17         List<String> diff = new ArrayList<String>();
    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         return diff;
    25     }
    千万不要采用这种方法,总共要循环的次数是两个List的size相乘的积,从输出看耗时也是比较长的,那么我们有没有其他的方法呢?当然有.

    方法2:采用List提供的retainAll()方法:
     1 public static void main(String[] args) {
     2         List<String> list1 = new ArrayList<String>();
     3         List<String> list2 = new ArrayList<String>();
     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<String> getDiffrent2(List<String> list1, List<String> list2) {
    13         long startTime = System.currentTimeMillis();
    14         list1.retainAll(list2);
    15         System.out.println("Total Time: " + (System.currentTimeMillis() - startTime));
    16         return list1;
    17     }

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

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

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

     1     private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
     2         long startTime = System.currentTimeMillis();
     3         Map<String, Integer> map = new HashMap<String, Integer>(list1.size() + list2.size());
     4         List<String> diff = new ArrayList<String>();
     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.Entry<String, Integer> entry : map.entrySet()) {
    17             if (entry.getValue() == 1) {
    18                 diff.add(entry.getKey());
    19             }
    20         }
    21         System.out.println("Total Time: " + (System.currentTimeMillis() - startTime));
    22         return list1;
    23     }
    显然,这种方法大大减少耗时,是方法1的1/4,是方法2的1/40,这个性能的提升时相当可观的,但是,这不是最佳的解决方法,观察方法3我们只是随机取了一个list作为首次添加的标准,这样一旦我们的list2比list1的size大,则我们第二次put时的if判断也会耗时,做如下改进:
     1 private static List<String> getDiffrent4(List<String> list1, List<String> list2) {
     2         long st = System.nanoTime();
     3         Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
     4         List<String> diff = new ArrayList<String>();
     5         List<String> maxList = list1;
     6         List<String> 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.Entry<String, Integer> entry: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         return diff;
    33         
    34     }

    这里对连个list的大小进行了判断,小的在最后添加,这样会减少循环里的判断,性能又有了一定的提升,正如一位朋友所说,编程是无止境的,只要你认真去思考了,总会找到更好的方法!
    非常感谢binglian的指正,针对List有重复元素的问题,做以下修正,首先明确一点,两个List不管有多少个重复,只要重复的元素在两个List都能找到,则不应该包含在返回值里面,所以在做第二次循环时,这样判断:如果当前元素在map中找不到,则肯定需要添加到返回值中,如果能找到则value++,遍历完之后diff里面已经包含了只在list2里而没在list2里的元素,剩下的工作就是找到list1里有list2里没有的元素,遍历map取value为1的即可:

     1 private static List<String> getDiffrent5(List<String> list1, List<String> list2) {
     2         long st = System.nanoTime();
     3          List<String> diff = new ArrayList<String>();
     4          List<String> maxList = list1;
     5          List<String> minList = list2;
     6          if(list2.size()>list1.size())
     7          {
     8              maxList = list2;
     9              minList = list1;
    10          }
    11          Map<String,Integer> map = new HashMap<String,Integer>(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.Entry<String, Integer> entry: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         return diff;
    32         
    33     }
    
    
  • 相关阅读:
    python基础——单元测试
    python基础——调试
    python基础——错误处理
    python基础——使用元类
    python基础——多重继承
    python基础——使用@property
    sql server 2000的安装
    [转载]目前流行的缺陷管理工具
    个人工作中ssd、audio python脚本总结
    python Synchronization between processes
  • 原文地址:https://www.cnblogs.com/zi-yao/p/6224803.html
Copyright © 2011-2022 走看看