JAVA基础-List去重的6种方式

简述

java开发中经常会遇到List去重这个工作,现在就来整理一下List去重的6种方式。

方法代码以及效率测试

模拟测试数据

相关代码:

import java.util.LinkedList;
import java.util.List;

/**
 * @ClassName: ListCleatMain
 * @Description: 模拟数据进行List去重测试
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListCleatMain {
    public static void main(String[] args) {
        //声明一个新的List
        List<Integer> list = new LinkedList<>();
        //数据装载开始时间
        Long startTime = System.currentTimeMillis();
        System.out.println("数据装载开始时间:" + startTime);
        //模拟添加200000个Integer
        for (int i=0;i<200000;i++){
            Integer in =(int)(Math.random()*10+1);
            list.add(in);
        }
        //数据装载结束时间
        Long endTime = System.currentTimeMillis();
        System.out.println("数据装载结束时间:" + endTime);
        //数据装载时差
        System.out.println("数据装载时差:" + (endTime - startTime));

        //原数据遍历打印
        System.out.println("原数据:");
        list.forEach( li-> {
            System.out.print(li + "  ");
        });
        System.out.println();


        //开始时间
        long startClear = System.currentTimeMillis();
        System.out.println("开始时间:" + startClear);
        //去重-双for循环去重
        List newList = ListClearByDoubleFor.listRemove(list);
        //去重-Contains方法去重
//        List newList = ListClearByContains.listRemove(list);
        //去重-利用HashSet去重
//        List newList = ListClearByHashSet.listRemove(list);
        //去重-利用TreeSet去重
//        List newList = ListClearByTreeSet.listRemove(list);
        //去重-利用LinkedHashSet去重
//        List newList = ListClearByLinkedHashSet.listRemove(list);
        //去重-利用Java8的stream去重
//        List newList = ListClearByStream.listRemove(list);
        //结束时间
        long endClear = System.currentTimeMillis();
        System.out.println("结束时间:" + endClear);
        //时间差
        System.out.println("去重用时:" + (endClear - startClear));

        //去重后数据遍历打印
        System.out.println("新数据:");
        newList.forEach(li -> {
            System.out.print(li + "  ");
        });
    }
}

双for循环

实现思想:

使用两个for循环遍历集合所有元素,然后进行判断是否有相同元素,如果有,则去除。(有序)

相关代码:

import java.util.List;
/**
 * @ClassName: ListClearByDoubleFor
 * @Description: 双for循环进行list去重
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListClearByDoubleFor {
    /**
     * 数据去重
     * @param list
     * @return list
     */
    public static List listRemove(List<Integer> list){
        //双for循环进行list去重
        for (int i=0; i<list.size(); i++){
            for (int j=i+1; j<list.size(); j++){
                if (list.get(i).equals(list.get(j))){
                    list.remove(j);
                    j--;
                }
            }
        }
        return list;
    }
}

效率测试:

数据装载开始时间:1605667320390
数据装载结束时间:1605667320407
数据装载时差:17
原数据:
9  8  3  2  3  10  4  9  5  3  1  7  6  8  7  8  9  5  5  8  2  2 ……(20W数据)
开始时间:1605667320811
结束时间:1605667413269
去重用时:92458(这还用再测?)
新数据:
9  8  3  2  10  4  5  1  7  6
--------------------------------------------------
<!-- 200W(20以内)数据测试 -->
它不配!!!!!

List的contains方法

实现思想:

利用List集合contains方法循环遍历,先创建新的List集合,接着循环遍历原来的List集合,判断新集合是否包含有旧集合,如果有,则不添加至新集合,否则添加。(有序)

相关代码:

import java.util.ArrayList;
import java.util.List;
/**
 * @ClassName: ListClearByContains
 * @Description: 利用List集合contains方法循环遍历去重
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListClearByContains {
    /**
     * 数据去重
     * @param list
     * @return list
     */
    public static List listRemove(List list){
        //利用List集合contains方法循环遍历去重
        List newList = new ArrayList();
        list.forEach(li -> {
            //判断新集合是否包含有,如果不包含有,则存入新集合中
            if (!newList.contains(li)){
                newList.add(li);
            }
        });
        return newList;
    }
}

效率测试:

数据装载开始时间:1605674583323
数据装载结束时间:1605674583340
数据装载时差:17
原数据:
8  10  7  3  7  6  6  1  7  6  2  8  9  6  6  5  2  6  5  5  3  1 ……(20W数据)
开始时间:1605674583756
结束时间:1605674583771
去重用时:15/13/13/13/11(5次测试)
新数据:
8  10  7  3  6  1  2  9  5  4
--------------------------------------------------
<!-- 200W(20以内)数据测试 -->
开始时间:1605680194498
结束时间:1605680194553
去重用时:55/57/56/61/64(打印原数据)
去重用时:144/120/127/124/125(不打印原数据)
新数据:
17  3  2  16  13  6  18  1  10  5  11  7  20  9  15  14  12  8  19  4

HashSet方法

实现思想:

HashSet实现了Set接口,不允许出现重复元素。可以基于这个想法,把List集合所有元素存入HashSet对象,接着把List集合元素全部清空,最后把HashSet对象元素全部添加至List集合中,这样就可以保证不出现重复元素。而HashSet有一个构造函数,在初始化时可以直接添加元素。其中,HashSet不能保证顺序不变,所以此方式不能保证List集合原来的顺序不变。 (无序)

相关代码:

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
/**
 * @ClassName: ListClearByHashSet
 * @Description: List集合放入HashSet中利用HashSet实现Set接口的无重复元素特性去重
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListClearByHashSet {
    /**
     * 数据去重
     * @param list
     * @return list
     */
    public static List listRemove(List list){
        //List集合放入HashSet中利用HashSet实现Set接口的无重复元素特性去重
        HashSet set = new HashSet(list);
        list.clear();
        list.add(set);
//        Iterator it = set.iterator();
//        while (it.hasNext()){
//            list.add(it.next());
//        }
        return list;
    }
}

效率测试:

数据装载开始时间:1605675392758
数据装载结束时间:1605675392776
数据装载时差:18
原数据:
3  6  1  1  7  6  9  2  8  6  4  7  1  9  5  5  4  3  3  1  10  8  10  10……(20w数据)
开始时间:1605675393184
结束时间:1605675393200
去重用时:16/20/15/12/15(5次测试)
新数据:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
--------------------------------------------------
<!-- 200W(20以内)数据测试 -->
开始时间:1605680088596
结束时间:1605680088641
去重用时:45/45/46/52/44(打印原数据)
去重用时:167/163/160/164/166(不打印原数据)
新数据:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

TreeSet方法

实现思想:

TreeSet集合也是实现Set接口,是一个有序的,并且无重复元素集合。(有序)

相关代码:

import java.util.*;
/**
 * @ClassName: ListClearByTreeSet
 * @Description: List集合放入TreeSet中利用HashSet实现Set接口的无重复元素特性去重
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListClearByTreeSet {
    /**
     * 数据去重
     * @param list
     * @return list
     */
    public static List listRemove(List list){
        //List集合放入TreeSet中利用HashSet实现Set接口的无重复元素特性去重
        TreeSet set = new TreeSet(list);
        list.clear();
        list.add(set);
//        Iterator it = set.iterator();
//        while (it.hasNext()){
//            list.add(it.next());
//        }
        return list;
    }
}

效率测试:

数据装载开始时间:1605675683470
数据装载结束时间:1605675683487
数据装载时差:17
原数据:
8  10  4  3  8  10  8  2  1  9  1  3  1  1  8  3  4  4  10  6  6  2……(20W数据)
开始时间:1605675683881
结束时间:1605675683909
去重用时:28/25/29/26/26(5次测试)
新数据:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
--------------------------------------------------
<!-- 200W(20以内)数据测试 -->
开始时间:1605679953606
结束时间:1605679953697
去重用时:91/94/104/103/101(打印原数据)
去重用时:112/119/123/113/117(不打印原数据)
新数据:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

LinkedHashSet方法

实现思想:

LinkedHashSet是一个实现Set接口将ArrayList删除重复数据的最佳方法。LinkedHashSet在内部完成两件事:删除重复数据,保持添加到其中的数据的顺序。(有序)

相关代码:

import java.util.*;

/**
 * @ClassName: ListClearByLinkedHashSet
 * @Description: List集合放入LinkedHashSet中利用LinkedHashSet实现Set接口的无重复元素特性去重
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListClearByLinkedHashSet {
    /**
     * 数据去重
     * @param list
     * @return list
     */
    public static List listRemove(List list){
        //List集合放入LinkedHashSet中利用LinkedHashSet实现Set接口的无重复元素特性去重
        LinkedHashSet hashSet = new LinkedHashSet(list);
        list.clear();
        list.add(hashSet);
//        Iterator iterator = hashSet.iterator();
//        while(iterator.hasNext()){
//            list.add(iterator.next());
//        }
        return list;
    }
}

效率测试:

数据装载开始时间:1605675839400
数据装载结束时间:1605675839417
数据装载时差:17
原数据:
2  7  5  1  4  4  1  5  1  9  10  7  3  9  7  3  6  7  1  1  4……(20W数据)
开始时间:1605675839818
结束时间:1605675839835
去重用时:17/16/14/17/19(5次测试)
新数据:
[2, 7, 5, 1, 4, 9, 10, 3, 6, 8]
--------------------------------------------------
<!-- 200W(20以内)数据测试 -->
开始时间:1605679823889
结束时间:1605679823938
去重用时:49/43/42/42/44(打印原数据)
去重用时:173/159/160/163/169(不打印原数据)
新数据:
[12, 1, 9, 6, 3, 8, 19, 13, 5, 16, 2, 10, 11, 15, 17, 4, 7, 20, 14, 18]

Java8的stream方法

实现思想:

要从arraylist中删除重复项,我们也可以使用java 8 stream api。使用steam的distinct()方法返回一个由不同数据组成的流,通过对象的equals()方法进行比较。 (有序)

相关代码:

import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName: ListClearByStream
 * @Description: 使用java8新特性stream实现List去重
 * @author: ssqxx
 * @date: 2020-11-18
 * @version 1.0
 */
public class ListClearByStream {
    /**
     * 数据去重
     * @param list
     * @return list
     */
    public static List listRemove(List list){
        //使用java8新特性stream实现List去重
        List newList = (List) list.stream().distinct().collect(Collectors.toList());
        return newList;
    }
}

效率测试:

数据装载开始时间:1605679132825
数据装载结束时间:1605679132841
数据装载时差:16
原数据:
7  3  4  6  5  2  7  8  5  6  7  4  3  8  6  5  2  4  5  5  2  10  2……(20W数据)
开始时间:1605679133233
结束时间:1605679133248
去重用时:15/14/13/19/16(5次测试)
新数据:
7  3  4  6  5  2  8  10  9  1
--------------------------------------------------
<!-- 200W(20以内)数据测试 -->
开始时间:1605679582415
结束时间:1605679582443
去重用时:28/31/35/27/31(打印原数据)
去重用时:94/81/96/96/87(不打印原数据)
新数据:
8  15  4  3  17  1  10  19  12  9  16  20  7  6  18  13  2  5  14  11

结论

随机数在200000范围10以内(平均值):

  • 使用两个for循环实现List去重:94258毫秒
  • 使用List集合contains方法循环遍历去重时间:13毫秒
  • 使用HashSet实现List去重时间:16毫秒
  • 使用TreeSet实现List去重时间:27毫秒
  • 使用LinkedHashSet实现List去重时间:17毫秒
  • 使用java8新特性stream实现List去重:15毫秒

随机数在2000000范围20以内(平均值):

  • 使用两个for循环实现List去重:已放弃!!!!
  • 使用List集合contains方法循环遍历去重时间:128毫秒
  • 使用HashSet实现List去重时间:164毫秒
  • 使用TreeSet实现List去重时间:117毫秒
  • 使用LinkedHashSet实现List去重时间:165毫秒
  • 使用java8新特性stream实现List去重:91毫秒

随机数在20000000范围20以内(一次值):

  • 使用两个for循环实现List去重:已放弃!!!!
  • 使用List集合contains方法循环遍历去重时间:612毫秒
  • 使用HashSet实现List去重时间:334毫秒
  • 使用TreeSet实现List去重时间:813毫秒
  • 使用LinkedHashSet实现List去重时间:364毫秒
  • 使用java8新特性stream实现List去重:214毫秒

结论简述:

目前表现最好的是java8新特性stream实现的list去除,不论是数据量大小;

HashSet、TreeSet、LinkedHashSet都有实现Set接口,所以速度都不会很慢,但是在过了1000W这个数量的list后去重速度骤降,HashSet,LinkedHashSet依旧稳定快速;整体上HashSet>LinkedHashSet>TreeSet。不过HashSet是无序的,若想有序可以使用LinkedHashSet;

list集合的contains方法在数据量不大的时候能去重速度也挺快的,甚至能超过HashSet,但数据量大(500W)之后,去重速度骤降,数据量不大的list去重可以使用;

双for循环进行list去重就……算了,放弃吧!

整体排序:

500W数据以下:

  • (有序)stream>contains>LinkedHashSet>TreeSet>for
  • (无序)stream>contains>HashSet>LinkedHashSet>TreeSet>for

500W数据以上:

  • (有序)stream>LinkedHashSet>contains>TreeSet>for
  • (无序)stream>HashSet>LinkedHashSet>contains>TreeSet>for

以上数据为自测数据,可能与实际应用会存在误差,下次再测试一下String(字符串)的list排序。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容