Java集合之List去重

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

List 去重的方式较多,本人对常用的几种去重方式进行整理、分析,如有错漏欢迎指正。

HashSet

利用 Set 元素的不重复特性去重,去重后不保留原顺序。无法直接对新对象(new 创建的对象)去重。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码public class Test {
public static void main(String[] args) {
// 1.构造 List
List<Person> list = new ArrayList<>();
Person p = new Person("张三", 20, '男');
list.add(p);
list.add(new Person("小丽", 20, '女'));
list.add(new Person("小丽", 20, '女'));
// 2.去重
Set<Person> hashSet = new HashSet<>(list);
List<Person> newList = new ArrayList<>(hashSet);
System.out.println(newList);//没有对“小丽”去重
}
}

HashSet + ArrayList

通过 HashSet 判断元素是否重复,不重复则放入新的 List 中。这种方法去重后保留原顺序。无法直接对新对象(new 创建的对象)去重。

1
2
3
4
5
6
7
8
java复制代码Set<Person> hashSet = new HashSet<>();
List<Person> newList = new ArrayList<>();
for (Iterator<Person> iter = list.iterator(); iter.hasNext();) {
Person element = iter.next();
if (hashSet.add(element)) {
newList.add(element);
}
}

TreeSet

利用 TreeSet 的元素不重复特性去重,可自定义排序,默认自然排序

1
2
java复制代码Set<String> treeSet = new TreeSet<String>(list);
List<String> newList = new ArrayList<>(treeSet);

ArrayList

使用两个 List,遍历原 List,然后通过检查新 List 中是否存在原 List 中的元素来去重,这种去重保留原顺序。无法直接对新对象(new 创建的对象)去重。

1
2
3
4
5
6
java复制代码List<String> newList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (!newList.contains(list.get(i))) {
newList.add(list.get(i));
}
}

java8 的 stream

以流的方式去重会保留原顺序。无法直接对新对象(new 创建的对象)去重。

注意:流不会对原集合进行操作,所以要用新集合接收操作后的流。

1
java复制代码list.stream().distinct().collect(Collectors.toList());

实体单属性之自定义方法去重

上述方法不能根据实体的某个属性去重,因此只能通过自定义方法实现。利用流的 filter 来自定义方法,这种方式去重保留原顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码public class Test {
public static void main(String[] args) {
// 1.构造 List
...
// 2.去重
List<Person> newList = new ArrayList<>();
newList = list.stream()
.filter(distinctByKey(o -> o.getName() + ";" + o.getAge()))
.collect(Collectors.toList());
System.out.println(newList);
}

/**
* 自定义的去重方法
* @param <T> 待去重实体
* @param keyExtractor 去重标记(如:o.getName() + ";" + o.getAge())
* @return
*/
private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

}

实体单属性之 stream + TreeSet

stream + TreeSet,不保留原顺序,可自定义排序,默认自然排序

1
2
3
4
java复制代码List<Person> newList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(person -> person.getName() + ";" + person.getAge()))), ArrayList::new)
);

总结

stream 去重的效率最低,耗费时间大概为 HashSet + ArrayList 去重所耗时间的五倍。

建议:对性能要求不高使用 stream 方式去重,代码简洁;对性能有较高要求用 HashSet + ArrayList 或者 ArrayList ,后者性能略低,但是都比 HashSet 直接去重效率高;需要自定义排序用 TreeSet。

HashSet HashSet + ArrayList TreeSet ArrayList stream
写法 简单 较难 简单 较难 最简单
效率 较低 较低 最低
顺序 无序 原顺序 自然排序 原顺序 原顺序

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%