在Java开发中,List集合存储元素时允许重复,当业务需要保证元素唯一性时,就需要对List进行去重操作。不同的去重需求对应不同的实现方式,下面介绍几种常用的去重技巧。

基于HashSet的去重方式
HashSet本身不允许存储重复元素,利用这个特性可以快速实现List去重,但这种方式会丢失原有List的元素顺序。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ListDistinctDemo {
public static void main(String[] args) {
// 创建包含重复元素的List
List<String> originalList = new ArrayList<>();
originalList.add("apple");
originalList.add("banana");
originalList.add("apple");
originalList.add("orange");
originalList.add("banana");
// 利用HashSet去重
Set<String> tempSet = new HashSet<>(originalList);
List<String> distinctList = new ArrayList<>(tempSet);
System.out.println("去重后的List:" + distinctList);
}
}
基于LinkedHashSet的去重方式
如果需要保留原有List的元素顺序,可以使用LinkedHashSet,它既保证元素唯一性,又维持元素的插入顺序。
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class ListDistinctDemo2 {
public static void main(String[] args) {
List<Integer> originalList = new ArrayList<>();
originalList.add(1);
originalList.add(3);
originalList.add(2);
originalList.add(1);
originalList.add(3);
// 利用LinkedHashSet去重并保留顺序
Set<Integer> tempSet = new LinkedHashSet<>(originalList);
List<Integer> distinctList = new ArrayList<>(tempSet);
System.out.println("去重后保留顺序的List:" + distinctList);
}
}
基于Java 8 Stream API的去重方式
Java 8引入的Stream API提供了更简洁的去重写法,通过distinct()方法可以直接实现去重,默认会保留元素的原有顺序。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ListDistinctDemo3 {
public static void main(String[] args) {
List<String> originalList = new ArrayList<>();
originalList.add("cat");
originalList.add("dog");
originalList.add("cat");
originalList.add("bird");
originalList.add("dog");
// 使用Stream的distinct方法去重
List<String> distinctList = originalList.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("Stream去重后的List:" + distinctList);
}
}
自定义对象的去重方式
如果List中存储的是自定义对象,需要先重写对象的equals()和hashCode()方法,再去重才能保证逻辑正确。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
// 自定义用户类
class User {
private String id;
private String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
// 重写equals方法,根据id判断是否为同一个用户
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
// 重写hashCode方法,保证相同id的对象hashCode一致
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "User{id='" + id + "', name='" + name + "'}";
}
}
public class ListDistinctDemo4 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("1", "张三"));
userList.add(new User("2", "李四"));
userList.add(new User("1", "张三"));
userList.add(new User("3", "王五"));
userList.add(new User("2", "李四"));
// 自定义对象去重
Set<User> userSet = new HashSet<>(userList);
List<User> distinctUserList = new ArrayList<>(userSet);
System.out.println("自定义对象去重后的List:" + distinctUserList);
}
}
不同去重方式的对比
下面通过表格对比几种常用去重方式的特点,方便开发者选择:
| 去重方式 | 是否保留顺序 | 实现复杂度 | 适用场景 |
|---|---|---|---|
| HashSet | 否 | 低 | 不需要保留原有顺序,追求最快去重速度 |
| LinkedHashSet | 是 | 低 | 需要保留原有顺序,对顺序有要求 |
| Stream distinct() | 是 | 低 | 使用Java 8及以上版本,代码简洁性优先 |
| 自定义对象重写equals和hashCode | 根据使用的集合决定 | 中 | List存储自定义对象,需要根据业务规则去重 |
注意事项
- 使用HashSet或LinkedHashSet去重时,如果List元素为自定义对象,必须保证对象正确重写了
equals()和hashCode()方法,否则去重逻辑会不符合预期。 - Stream的
distinct()方法底层也是依赖元素的equals()方法判断重复,自定义对象同样需要重写相关方法。 - 如果List中元素数量非常大,HashSet的去重效率通常高于双重循环遍历的去重方式,因为HashSet的添加操作时间复杂度为O(1),而双重循环为O(n²)。