• 高效的找出两个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 }
    复制代码
  • 相关阅读:
    2008年末纪念:回顾改变Web的十大事记
    我的Silverlight初探
    查询表字段、类型、是否主键的sql脚本
    FOR XML AUTO将数据库表的一个字段的数据查询拼接为带间隔符的字符串
    Opera:发现浏览器安全漏洞后做什么
    Spry框架实现XML分页[原]
    sql带分隔符的字符串截取
    [转]Internet数据库连接器(IDC)技术
    [摘]淘宝网的开源架构
    System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本 XP
  • 原文地址:https://www.cnblogs.com/yangkai-cn/p/4016606.html
Copyright © 2020-2023  润新知