Java List去重 Lis集合去重 List去重效率对比 List去重复元素效率对比 List去重效率
--- List 去重复元素的几种办法
一、概述
面试的时候,有个常见的问题:“List集合如何去除重复元素”。 常见的回答是:“set集合,for循环对比,stream distinct”,那这些常见去重方法,哪个一个更好,哪一个效率更高呢?
本文将系统的整理,常见的集中List集合去重方法,使用Junit单元测试,对比效率去重效率。
二、代码实现
1、准备工作
构建一个 初始化List ,默认10w个元素,使用 StopWatch 作为计时器, @Before 注解,前置初始化集合元素, @After 注解,记录执行程序时间 , @Test 注解,执行测试工作。
final List<Integer> list = Lists.newArrayList();
// 总元素的个数
int count = 100_000;
// 重复元素
int repat = 5;
private StopWatch stopWatch;
@Before
public void init(){
for (int i = 0; i < count; i++) {
list.add(i);
}
final ArrayList<Integer> repatList = Lists.newArrayList();
for (int i = 0; i < repat ; i++) {
int n = (int)(Math.random()*count);
repatList.add(n);
}
list.addAll(repatList);
System.out.println("list 初始化完毕 size = "+ list.size());
System.out.println("重复元素是:"+ repatList);
System.out.println("开始记录时间 === ");
stopWatch = StopWatch.createStarted();
}
// === 执行单元测试方法 ...
@Test
public void test() throws Exception{
}
@After
public void after() throws Exception {
stopWatch.stop();
System.out.println("测试完毕,实际耗时:" +stopWatch.getTime() +" ,ms");
}
2、for 循环添加去重,list集合
@Test
public void forAddTest() throws Exception {
final ArrayList<Integer> destList = Lists.newArrayList();
for (Integer e : list) {
if (!destList.contains(e)) {
destList.add(e);
}
}
System.out.println("forAddTest ,去重后,集合元素个数 :" + destList.size());
}
2.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[43491, 7220, 42984, 65619, 35564]
开始记录时间 ===
forAddTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:5187 ,ms
Process finished with exit code 0
3、for 循环添加去重,set 集合
@Test
public void forAddSetTest() throws Exception {
final LinkedHashSet<Object> set = Sets.newLinkedHashSet();
for (Integer e : list) {
if (!set.contains(e)) {
set.add(e);
}
}
System.out.println("forAddSetTest ,去重后,集合元素个数 :" + set.size());
}
3.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[18748, 56260, 94869, 65498, 352]
开始记录时间 ===
forAddSetTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:28 ,ms
4、for 双循环去重,remove去重
@Test
public void forRemoveTest() throws Exception {
for (int i = 0; i < list.size() - 1; i++) {
for (int j = list.size() - 1; j > i; j--) {
if (list.get(j).equals(list.get(i))) {
list.remove(j);
}
}
}
System.out.println("forRemoveTest ,去重后,集合元素个数 :" + list.size());
}
4.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[24090, 85604, 21849, 26039, 34851]
开始记录时间 ===
forRemoveTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:12475 ,ms
Process finished with exit code 0
5、 for 循环去重复,index索引去重
@Test
public void forIndexRemoveTest() throws Exception {
List<Integer> list2 = new ArrayList(list);
for (Integer element : list2) {
if (list.indexOf(element) != list.lastIndexOf(element)) {
list.remove(list.lastIndexOf(element));
}
}
System.out.println("forIndexRemoveTest ,去重后,集合元素个数 :" + list.size());
}
5.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[24097, 8550, 83199, 66946, 83527]
开始记录时间 ===
forIndexRemoveTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:11679 ,ms
6、转换到set集合中去重
@Test
public void toSetTest() throws Exception {
final HashSet<Integer> set = Sets.newHashSet(list);
final ArrayList<Integer> integers = Lists.newArrayList(set);
System.out.println("toSetTest ,去重后,集合元素个数 :" + integers.size());
}
6.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[97016, 15751, 98344, 48053, 70403]
开始记录时间 ===
toSetTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:29 ,ms
7、使用Stream流,toSet集合中 去重
@Test
public void streamToSetTest() throws Exception {
final List<Integer> collect = list.stream().collect(Collectors.toSet()).stream().collect(Collectors.toList());
System.out.println("streamToSetTest ,去重后,集合元素个数 :" + collect.size());
}
7.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[14265, 81375, 57191, 21029, 77598]
开始记录时间 ===
streamToSetTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:66 ,ms
8、使用 Stream流,Distinct 中进行去重
@Test
public void streamDistinctTest() throws Exception {
final List<Integer> collect = list.stream().distinct().collect(Collectors.toList());
System.out.println("streamDistinctTest ,去重后,集合元素个数 :" + collect.size());
}
8.1、输出结果:
list 初始化完毕 size = 100005
重复元素是:[97197, 34832, 14705, 57613, 14578]
开始记录时间 ===
streamDistinctTest ,去重后,集合元素个数 :100000
测试完毕,实际耗时:61 ,ms
三、总结
1、从整体执行效率上看: Stream Distinct 方法, 效率最高,其次就是转换为 set,
for循环对比,是效率最低的,不可取!
stream Distinct > toSet > streamToSet > forAdd > forIndexRemove > forRemove
2、注意:forSetTest 比 forAddSetTest 效率低,原因是: list.contains和 set.contains方法 ,底层实现不同,list.contains 方法是逐个遍历判断,效率低下。
2.1、注意点:若是普通对象去重,则需要 手动重写equals和hashCode方法,否则会出现去重无效的情况!
若是List<User> 对象去重,重写 User对象的equals和hashCode方法
3、面试的时候建议回答: JDK8之前,放到set集合中; JDK8及以后,使用 Stream distinct 方法。