JAVA基础-List去重的6种方式
简述
java开发中经常会遇到List去重这个工作,现在就来整理一下List去重的6种方式。
方法代码以及效率测试
模拟测试数据
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import java.util.LinkedList; import java.util.List;
public class ListCleatMain { public static void main(String[] args) { List<Integer> list = new LinkedList<>(); Long startTime = System.currentTimeMillis(); System.out.println("数据装载开始时间:" + startTime); 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); List newList = ListClearByDoubleFor.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循环遍历集合所有元素,然后进行判断是否有相同元素,如果有,则去除。(有序)
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.util.List;
public class ListClearByDoubleFor {
public static List listRemove(List<Integer> 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; } }
|
效率测试:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 数据装载开始时间: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集合,判断新集合是否包含有旧集合,如果有,则不添加至新集合,否则添加。(有序)
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.util.ArrayList; import java.util.List;
public class ListClearByContains {
public static List listRemove(List list){ List newList = new ArrayList(); list.forEach(li -> { if (!newList.contains(li)){ newList.add(li); } }); return newList; } }
|
效率测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 数据装载开始时间: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集合原来的顺序不变。 (无序)
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.util.HashSet; import java.util.Iterator; import java.util.List;
public class ListClearByHashSet {
public static List listRemove(List list){ HashSet set = new HashSet(list); list.clear(); list.add(set);
return list; } }
|
效率测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 数据装载开始时间: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接口,是一个有序的,并且无重复元素集合。(有序)
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import java.util.*;
public class ListClearByTreeSet {
public static List listRemove(List list){ TreeSet set = new TreeSet(list); list.clear(); list.add(set);
return list; } }
|
效率测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 数据装载开始时间: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在内部完成两件事:删除重复数据,保持添加到其中的数据的顺序。(有序)
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.util.*;
public class ListClearByLinkedHashSet {
public static List listRemove(List list){ LinkedHashSet hashSet = new LinkedHashSet(list); list.clear(); list.add(hashSet);
return list; } }
|
效率测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 数据装载开始时间: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()方法进行比较。 (有序)
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.util.List; import java.util.stream.Collectors;
public class ListClearByStream {
public static List listRemove(List list){ List newList = (List) list.stream().distinct().collect(Collectors.toList()); return newList; } }
|
效率测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 数据装载开始时间: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排序。