Java 你还在用lsit.contain做去重? 你是故意的还是不小心的?

Posted 小目标青年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 你还在用lsit.contain做去重? 你是故意的还是不小心的?相关的知识,希望对你有一定的参考价值。

前言

最近又是一轮代码review , 发现了一些实现去重的代码,在使用 lsit.contain ......

如:

我沉思,是不是其实很多初学者也存在这种去重使用问题?

所以我选择把这个事情整出来,分享一下。

正文


首先是造出一个 List<String> 模拟数据,一共2W条,里面有一半数据1W条是重复的:

    public static List<String> getTestList() 
        List<String> list = new ArrayList<>();
        for (int i = 1; i <= 10000; i++) 
            list.add(String.valueOf(i));
        
        for (int i = 10000; i >= 1; i--) 
            list.add(String.valueOf(i));
        
        return list;
    

先看看 我们用contain 去重的 代码:

    /**
     * 使用 list.contain 去重
     *
     * @param testList
     */
    private static void useContain2Distinct(List<String> testList) 
        System.out.println("contains 开始去重,条数:" + testList.size());
        List<String> testListDistinctResult = new ArrayList<>();
        for (String str : testList) 
            if (!testListDistinctResult.contains(str)) 
                testListDistinctResult.add(str);
            
        
        System.out.println("contains 去重完毕,条数:" + testListDistinctResult.size());
    

我们调用一下看看耗时:

    public static void main(String[] args) 
        List<String> testList = getTestList();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        useContainDistinct(testList);
        stopWatch.stop();
        System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
    

耗时:

 评价: list.contain 的效率,我的建议是,知道就行,别用。


众所周知Set 不存在 重复数据, 所以我们来看看 使用HashSet去重的性能:
ps: 这里是采取使用 set的add 方法做去重

    /**
     * 使用set去重
     *
     * @param testList
     */
    private static void useSetDistinct(List<String> testList) 
        System.out.println("HashSet.add 开始去重,条数:" + testList.size());
        List<String> testListDistinctResult = new ArrayList<>(new HashSet(testList));
        System.out.println("HashSet.add 去重完毕,条数:" + testListDistinctResult.size());
    

我们调用一下看看耗时:

    public static void main(String[] args) 
        List<String> testList = getTestList();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        useSetDistinct(testList);
        stopWatch.stop();
        System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
    

耗时:


 

评价:HashSet 的效率,我的建议是,推荐。


 

为什么耗时 差距这么大?

不多说,我们看源码:

list.contains(o):

 可以看到里面用到了 index(o) :

时间复杂度 : O(n) n: 元素个数

那么我们看看 set.add(o) 是怎么样的 :

map的add , 老生常谈就不谈了,hash完 直接塞到某个位置, 时间复杂度 : O(1)  。

所以 O(n) 和  O(1) 谁快  谁慢 ? 显然。

 

ps: 顺嘴说下 hashset的 contain  

时间复杂度也是 : O(1)   

那么我们最后再看看别的去重:
 

双for循环 ,remove去重 

    /**
     * 使用双for循环去重
     * @param testList
     */
    private static void use2ForDistinct(List<String> testList) 
        System.out.println("list 双循环 开始去重,条数:" + testList.size());
        for (int i = 0; i < testList.size(); i++) 
            for (int j = i + 1; j < testList.size(); j++) 
                if (testList.get(i).equals(testList.get(j))) 
                    testList.remove(j);
                
            
        
        System.out.println("list 双循环  去重完毕,条数:" + testList.size());
    
    public static void main(String[] args) 
        List<String> testList = getTestList();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        use2ForDistinct(testList);
        stopWatch.stop();
        System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
    

耗时:

 评价:知道就行,图个乐,别用,贼慢,而且代码看起来乱:。


 

stream的distinct去重:
 

    /**
     * 使用Stream 去重
     *
     * @param testList
     */
    private static void useStreamDistinct(List<String> testList) 
        System.out.println("stream 开始去重,条数:" + testList.size());
        List<String> testListDistinctResult = testList.stream().distinct().collect(Collectors.toList());
        System.out.println("stream 去重完毕,条数:" + testListDistinctResult.size());
    
    public static void main(String[] args) 
        List<String> testList = getTestList();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        useStreamDistinct(testList);
        stopWatch.stop();
        System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
    

耗时:

 

 评价:还不错,主要是代码也蛮简洁,有一点点动心。

好了,该篇就到这。

Java 12 来了, 你还在用 Java 8 吗?

点击上面「蓝色微信名」可快速关注


去年 9 月,Oracle 推出了一个长期支持的版本 Java 11 (18.9 LTS),时隔半年,最新版本的 Java 12 已于美国当地时间 3 月 19 日与大家见面。Java 12 是一个短期支持版本,可以在下一个版本发布之前获得 Oracle 的商业支持

 

Java 自 1995 年诞生以来已经走过了 24 个年头,它的实用性和兼容性无时无刻不彰显着其价值。Java 是 TIOBE 编程语言排行榜的常客,经常占据排行榜的头把交椅。

 

虽然 Java 版本的更新速度很快,但现在很多开发者使用的版本仍然是 Java 8,虽然有点陈旧,但在进行项目开发时,大家最关心的是编程语言的可靠性和稳定性,因为谁也不想让自己辛辛苦苦盘下的代码付诸东流。Java 12 来了以下 8 个重大新功能:



Java 12
八大新功能


189 Shenandoah: A Low-Pause-Time GarbageCollector (Experimental) 低暂停时间的GC230 Microbenchmark Suite 微基准测试套件325 Switch Expressions (Preview) Switch表达式334 JVM Constants API JVM常量API340 One AArch64 Port, Not Two 只保留一个AArch64实现341 Default CDS Archives 默认类数据共享归档文件344 Abortable Mixed Collections for G1 可中止的G1 Mixed GC346 Promptly Return Unused Committed Memoryfrom G1 G1及时返回未使用的已分配内存


目前,全球有超过 30 亿台设备运行 Java 语言。据 IDC 的统计数字,在所有软件开发类人才的需求中,对Java工程师的需求达到全部需求量的 60~70%我国目前对软件人才的需求已达 400 万,并且每年都有 20% 的增量,未来几年内,合格的软件人才需求将远远大于供给。

 

我们也为大家提供了有关 Java 的培训内容,欢迎大家阅读。





更多内容敬请期待!




今日小调查





以上是关于Java 你还在用lsit.contain做去重? 你是故意的还是不小心的?的主要内容,如果未能解决你的问题,请参考以下文章

你还在用迭代器处理集合吗?试试Stream,真香!

Java - 你还在用 if...else... 初始化集合为 null 情况?

你还在用 Java 8?手把手教你从 Java 8 升级到 Java 17 全过程,真香!!

你还在用for循环遍历list吗?

JDK11都发布了,你还在用JDK6?

MySQL 去重的 3 种方法​,还有谁不会?!