zoukankan      html  css  js  c++  java
  • 高效的找出两个List中的不同元素

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

    方法1:遍历两个集合:

    package com.czp.test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestList {
    
        public static void main(String[] args) {
            List<String> list1 = new ArrayList<String>();
            List<String> list2 = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                list1.add("test"+i);
                list2.add("test"+i*2);
            }
            getDiffrent(list1,list2);
            //输出:total times 2566454675
        }
    
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            List<String> diff = new ArrayList<String>();
            for(String str:list1)
            {
                if(!list2.contains(str))
                {
                    diff.add(str);
                }
            }
            System.out.println("total times "+(System.nanoTime()-st));
            return diff;
        }
    }

    千万不要采用这种方法,总共要循环的次数是两个List的size相乘的积,从输出看耗时也是比较长的,那么我们有没有其他的方法呢?当然有.

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

    package com.czp.test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TestList {
    
        public static void main(String[] args) {
            List<String> list1 = new ArrayList<String>();
            List<String> list2 = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                list1.add("test"+i);
                list2.add("test"+i*2);
            }
            getDiffrent(list1,list2);
            //输出:total times 2566454675
            getDiffrent2(list1,list2);
            //输出:getDiffrent2 total times 2787800964
        }
        
        /**
         * 获取连个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            list1.retainAll(list2);
            System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
            return list1;
        }
    
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            List<String> diff = new ArrayList<String>();
            for(String str:list1)
            {
                if(!list2.contains(str))
                {
                    diff.add(str);
                }
            }
            System.out.println("getDiffrent total times "+(System.nanoTime()-st));
            return diff;
        }
    }

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

     public boolean retainAll(Collection<?> c) {
        boolean modified = false;
        Iterator<E> e = iterator();
        while (e.hasNext()) {
            if (!c.contains(e.next())) {
            e.remove();
            modified = true;
            }
        }
        return modified;
        }

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

    package com.czp.test;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class TestList {
    
        public static void main(String[] args) {
            List<String> list1 = new ArrayList<String>();
            List<String> list2 = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                list1.add("test"+i);
                list2.add("test"+i*2);
            }
            getDiffrent(list1,list2);
            //输出:total times 2566454675
            getDiffrent2(list1,list2);
            //输出:getDiffrent2 total times 2787800964
            getDiffrent3(list1,list2);
            //输出:getDiffrent3 total times 61763995
        }
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
            List<String> diff = new ArrayList<String>();
            for (String string : list1) {
                map.put(string, 1);
            }
            for (String string : list2) {
                Integer cc = map.get(string);
                if(cc!=null)
                {
                    map.put(string, ++cc);
                    continue;
                }
                map.put(string, 1);
            }
            for(Map.Entry<String, Integer> entry:map.entrySet())
            {
                if(entry.getValue()==1)
                {
                    diff.add(entry.getKey());
                }
            }
            System.out.println("getDiffrent3 total times "+(System.nanoTime()-st));
            return list1;
        }
    
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            list1.retainAll(list2);
            System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
            return list1;
        }
    
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            List<String> diff = new ArrayList<String>();
            for(String str:list1)
            {
                if(!list2.contains(str))
                {
                    diff.add(str);
                }
            }
            System.out.println("getDiffrent total times "+(System.nanoTime()-st));
            return diff;
        }
    }

    显然,这种方法大大减少耗时,是方法1的1/4,是方法2的1/40,这个性能的提升时相当可观的,但是,这不是最佳的解决方法,观察方法3我们只是随机取了一个list作为首次添加的标准,这样一旦我们的list2比list1的size大,则我们第二次put时的if判断也会耗时,做如下改进:

    package com.czp.test;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class TestList {
    
        public static void main(String[] args) {
            List<String> list1 = new ArrayList<String>();
            List<String> list2 = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                list1.add("test"+i);
                list2.add("test"+i*2);
            }
            getDiffrent(list1,list2);
            getDiffrent2(list1,list2);
            getDiffrent3(list1,list2);
            getDiffrent4(list1,list2);
    //        getDiffrent total times 2789492240
    //        getDiffrent2 total times 3324502695
    //        getDiffrent3 total times 24710682
    //        getDiffrent4 total times 15627685
        }
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent4(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
            List<String> diff = new ArrayList<String>();
            List<String> maxList = list1;
            List<String> minList = list2;
            if(list2.size()>list1.size())
            {
                maxList = list2;
                minList = list1;
            }
            for (String string : maxList) {
                map.put(string, 1);
            }
            for (String string : minList) {
                Integer cc = map.get(string);
                if(cc!=null)
                {
                    map.put(string, ++cc);
                    continue;
                }
                map.put(string, 1);
            }
            for(Map.Entry<String, Integer> entry:map.entrySet())
            {
                if(entry.getValue()==1)
                {
                    diff.add(entry.getKey());
                }
            }
            System.out.println("getDiffrent4 total times "+(System.nanoTime()-st));
            return diff;
            
        }
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
            List<String> diff = new ArrayList<String>();
            for (String string : list1) {
                map.put(string, 1);
            }
            for (String string : list2) {
                Integer cc = map.get(string);
                if(cc!=null)
                {
                    map.put(string, ++cc);
                    continue;
                }
                map.put(string, 1);
            }
            for(Map.Entry<String, Integer> entry:map.entrySet())
            {
                if(entry.getValue()==1)
                {
                    diff.add(entry.getKey());
                }
            }
            System.out.println("getDiffrent3 total times "+(System.nanoTime()-st));
            return diff;
        }
    
        /**
         * 获取连个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            list1.retainAll(list2);
            System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
            return list1;
        }
    
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            List<String> diff = new ArrayList<String>();
            for(String str:list1)
            {
                if(!list2.contains(str))
                {
                    diff.add(str);
                }
            }
            System.out.println("getDiffrent total times "+(System.nanoTime()-st));
            return diff;
        }
    }

    这里对连个list的大小进行了判断,小的在最后添加,这样会减少循环里的判断,性能又有了一定的提升,正如一位朋友所说,编程是无止境的,只要你认真去思考了,总会找到更好的方法!

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

    package com.czp.test;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class TestList {
    
        public static void main(String[] args) {
            List<String> list1 = new ArrayList<String>();
            List<String> list2 = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                list1.add("test"+i);
                list2.add("test"+i*2);
            }
            getDiffrent(list1,list2);
            getDiffrent3(list1,list2);
            getDiffrent5(list1,list2);
            getDiffrent4(list1,list2);
            getDiffrent2(list1,list2);
    
    //        getDiffrent3 total times 32271699
    //        getDiffrent5 total times 12239545
    //        getDiffrent4 total times 16786491
    //        getDiffrent2 total times 2438731459
            
        }
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent5(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
             List<String> diff = new ArrayList<String>();
             List<String> maxList = list1;
             List<String> minList = list2;
             if(list2.size()>list1.size())
             {
                 maxList = list2;
                 minList = list1;
             }
             Map<String,Integer> map = new HashMap<String,Integer>(maxList.size());
             for (String string : maxList) {
                 map.put(string, 1);
             }
             for (String string : minList) {
                 if(map.get(string)!=null)
                 {
                     map.put(string, 2);
                     continue;
                 }
                 diff.add(string);
             }
             for(Map.Entry<String, Integer> entry:map.entrySet())
             {
                 if(entry.getValue()==1)
                 {
                     diff.add(entry.getKey());
                 }
             }
            System.out.println("getDiffrent5 total times "+(System.nanoTime()-st));
            return diff;
            
        }
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent4(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
            List<String> diff = new ArrayList<String>();
            List<String> maxList = list1;
            List<String> minList = list2;
            if(list2.size()>list1.size())
            {
                maxList = list2;
                minList = list1;
            }
            for (String string : maxList) {
                map.put(string, 1);
            }
            for (String string : minList) {
                Integer cc = map.get(string);
                if(cc!=null)
                {
                    map.put(string, ++cc);
                    continue;
                }
                map.put(string, 1);
            }
            for(Map.Entry<String, Integer> entry:map.entrySet())
            {
                if(entry.getValue()==1)
                {
                    diff.add(entry.getKey());
                }
            }
            System.out.println("getDiffrent4 total times "+(System.nanoTime()-st));
            return diff;
            
        }
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent3(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            Map<String,Integer> map = new HashMap<String,Integer>(list1.size()+list2.size());
            List<String> diff = new ArrayList<String>();
            for (String string : list1) {
                map.put(string, 1);
            }
            for (String string : list2) {
                Integer cc = map.get(string);
                if(cc!=null)
                {
                    map.put(string, ++cc);
                    continue;
                }
                map.put(string, 1);
            }
            for(Map.Entry<String, Integer> entry:map.entrySet())
            {
                if(entry.getValue()==1)
                {
                    diff.add(entry.getKey());
                }
            }
            System.out.println("getDiffrent3 total times "+(System.nanoTime()-st));
            return diff;
        }
    
        /**
         * 获取连个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent2(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            list1.retainAll(list2);
            System.out.println("getDiffrent2 total times "+(System.nanoTime()-st));
            return list1;
        }
    
        /**
         * 获取两个List的不同元素
         * @param list1
         * @param list2
         * @return
         */
        private static List<String> getDiffrent(List<String> list1, List<String> list2) {
            long st = System.nanoTime();
            List<String> diff = new ArrayList<String>();
            for(String str:list1)
            {
                if(!list2.contains(str))
                {
                    diff.add(str);
                }
            }
            System.out.println("getDiffrent total times "+(System.nanoTime()-st));
            return diff;
        }
    }

    以下是完整的代码:

     1 package com.czp.util;
     2 
     3 import java.util.Collection;
     4 import java.util.HashMap;
     5 import java.util.HashSet;
     6 import java.util.LinkedList;
     7 import java.util.Map;
     8 
     9 /**
    10  * 该类提供对集合类的高效操作
    11  * @author Czp
    12  *
    13  */
    14 
    15 public class CollectionUtil {
    16 
    17     /**
    18      * 不允许实例化
    19      */
    20     private CollectionUtil() {
    21     }
    22     
    23     /**
    24      *  获取两个集合的不同元素
    25      * @param collmax
    26      * @param collmin
    27      * @return
    28      */
    29     @SuppressWarnings({ "rawtypes", "unchecked" })
    30     public static Collection getDiffent(Collection collmax,Collection collmin)
    31     {
    32         //使用LinkeList防止差异过大时,元素拷贝
    33         Collection csReturn = new LinkedList();
    34         Collection max = collmax;
    35         Collection min = collmin;
    36         //先比较大小,这样会减少后续map的if判断次数
    37         if(collmax.size()<collmin.size())
    38         {
    39             max = collmin;
    40             min = collmax;
    41         }
    42         //直接指定大小,防止再散列
    43         Map<Object,Integer> map = new HashMap<Object,Integer>(max.size());
    44         for (Object object : max) {
    45             map.put(object, 1);
    46         }
    47         for (Object object : min) {
    48             if(map.get(object)==null)
    49             {
    50                 csReturn.add(object);
    51             }else{
    52                 map.put(object, 2);
    53             }
    54         }
    55         for (Map.Entry<Object, Integer> entry : map.entrySet()) {
    56             if(entry.getValue()==1)
    57             {
    58                 csReturn.add(entry.getKey());
    59             }
    60         }
    61         return csReturn;
    62     }
    63     /**
    64      *  获取两个集合的不同元素,去除重复
    65      * @param collmax
    66      * @param collmin
    67      * @return
    68      */
    69     @SuppressWarnings({ "rawtypes", "unchecked" })
    70     public static Collection getDiffentNoDuplicate (Collection collmax,Collection collmin)
    71     {
    72         return new HashSet(getDiffent(collmax, collmin));
    73     }
    74 }

      

     

  • 相关阅读:
    SharePoint 2013 图文开发系列之自定义字段
    SharePoint 2013 图文开发系列之Visual Studio 创建母版页
    SharePoint 2013 图文开发系列之代码定义列表
    SharePoint 2013 图文开发系列之计时器任务
    SharePoint 2013 图文开发系列之应用程序页
    SharePoint 2013 图文开发系列之事件接收器
    SharePoint 2013 图文开发系列之可视化WebPart
    SharePoint 2013 图文开发系列之WebPart
    SharePoint 2013 对二进制大型对象(BLOB)进行爬网
    SharePoint 2013 状态机工作流之日常报销示例
  • 原文地址:https://www.cnblogs.com/czpblog/p/2625794.html
Copyright © 2011-2022 走看看