这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
List 去重的方式较多,本人对常用的几种去重方式进行整理、分析,如有错漏欢迎指正。
HashSet
利用 Set 元素的不重复特性去重,去重后不保留原顺序。无法直接对新对象(new 创建的对象)去重。
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 创建的对象)去重。
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 的元素不重复特性去重,可自定义排序,默认自然排序。
Set<String> treeSet = new TreeSet<String>(list);
List<String> newList = new ArrayList<>(treeSet);
ArrayList
使用两个 List,遍历原 List,然后通过检查新 List 中是否存在原 List 中的元素来去重,这种去重保留原顺序。无法直接对新对象(new 创建的对象)去重。
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 创建的对象)去重。
注意:流不会对原集合进行操作,所以要用新集合接收操作后的流。
list.stream().distinct().collect(Collectors.toList());
实体单属性之自定义方法去重
上述方法不能根据实体的某个属性去重,因此只能通过自定义方法实现。利用流的 filter 来自定义方法,这种方式去重保留原顺序。
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,不保留原顺序,可自定义排序,默认自然排序。
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 | |
|---|---|---|---|---|---|
| 写法 | 简单 | 较难 | 简单 | 较难 | 最简单 |
| 效率 | 较低 | 高 | 较低 | 高 | 最低 |
| 顺序 | 无序 | 原顺序 | 自然排序 | 原顺序 | 原顺序 |