在Java开发中,处理重复数据是一项常见任务,无论是从集合、数组还是数据库查询结果中去除重复项,都需要选择合适的方法以确保代码的高效性和可读性,本文将系统介绍Java中去除重复数据的多种方式,涵盖基础集合操作、Stream API应用、数据库去重以及自定义对象去重等场景,帮助开发者根据实际需求选择最优解决方案。

使用Set集合自动去重的基本原理
Set集合是Java中用于存储不重复元素的核心数据结构,其基于HashMap实现,通过元素的hashCode()和equals()方法确保唯一性,最简单的去重方式是将包含重复数据的集合(如List)转换为Set,再转回原集合类型。
对于Integer类型的List去重,可直接通过HashSet实现:
List<Integer> list = Arrays.asList(1, 2, 2, 3, 4, 4, 5); Set<Integer> set = new HashSet<>(list); List<Integer> uniqueList = new ArrayList<>(set);
此方法利用HashSet不允许重复元素的特性,自动过滤掉重复数据,需要注意的是,如果List中存储的是自定义对象,必须正确重写该对象的hashCode()和equals()方法,否则去重逻辑会失效。
class Person {
private String name;
private int age;
// 省略构造方法、getter和setter
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
只有当两个对象的name和age属性都相同时,HashSet才会认为它们是重复元素。
Stream API实现去重的灵活应用
Java 8引入的Stream API为去重操作提供了更灵活的语法支持,尤其适合处理复杂业务逻辑,Stream的去重方法主要有distinct()和Collectors.toMap()两种方式。
基于元素的distinct()方法
distinct()是Stream中最直接的去重方法,它依赖元素的equals()方法判断重复性,适用于基本类型和包装类型:
List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Charlie", "Bob");
List<String> uniqueNames = names.stream()
.distinct()
.collect(Collectors.toList());
// 结果:[Alice, Bob, Charlie]
对于自定义对象,同样需要重写equals()方法,否则distinct()仅比较对象引用而非内容。
基于特定属性的toMap()去重
当需要根据对象的某个属性去重时,可通过Collectors.toMap()实现:
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Alice", 25)
);
Map<String, Person> uniquePeople = people.stream()
.collect(Collectors.toMap(
Person::getName, // 去重依据的属性(name)
person -> person,
(existing, replacement) -> existing // 遇到重复时的合并逻辑(保留前者)
));
List<Person> result = new ArrayList<>(uniquePeople.values());
此方法通过指定键提取器(如Person::getName)将去重逻辑聚焦到特定属性,避免因其他属性差异导致误判,若需保留重复元素中的后者,只需修改合并逻辑为(existing, replacement) -> replacement。

数组去重的实现方式
数组本身不支持直接去重,需先转换为集合或使用循环处理,以下是两种常用方法:
转换为Set集合去重
将数组转为List后,通过HashSet去重,再转回数组:
Integer[] array = {1, 2, 2, 3, 4, 4, 5};
Set<Integer> set = new HashSet<>(Arrays.asList(array));
Integer[] uniqueArray = set.toArray(new Integer[0]);
使用Java 8 Stream API去重
对于基本类型数组,可通过Stream的distinct()方法处理:
int[] intArray = {1, 2, 2, 3, 4, 4, 5};
int[] uniqueIntArray = Arrays.stream(intArray)
.distinct()
.toArray();
此方法语法简洁,且支持并行流处理(parallelStream()),适合大数据量场景。
数据库查询结果去重的实践
从数据库获取数据时,可通过SQL语句直接去重,或在Java层处理,推荐优先使用SQL优化性能,减少数据传输量。
SQL层去重
在查询语句中使用DISTINCT关键字或GROUP BY子句:
-- 使用DISTINCT SELECT DISTINCT name FROM users; -- 使用GROUP BY SELECT name FROM users GROUP BY name;
对于多字段去重,可同时指定多个列:SELECT DISTINCT name, age FROM users;。
Java层处理查询结果
若因业务限制无法在SQL层去重,可将结果转为List后使用前述集合或Stream方法处理,通过MyBatis查询用户列表后去重:
List<User> users = userMapper.selectAllUsers();
List<User> uniqueUsers = users.stream()
.collect(Collectors.toMap(
User::getName,
user -> user,
(existing, replacement) -> existing
))
.values()
.stream()
.collect(Collectors.toList());
大数据量场景下的去重优化
面对海量数据,需考虑内存占用和执行效率,以下是优化建议:

-
选择合适的数据结构:
- 对于基本类型,
HashSet比LinkedHashSet内存效率更高,后者因维护插入顺序需额外空间。 - 若需保持元素原始顺序,可使用
LinkedHashSet或Stream.distinct()(底层通过LinkedHashSet实现)。
- 对于基本类型,
-
并行流处理:
当数据量超过10万条时,可通过并行流加速去重:List<Integer> largeList = ...; // 大数据量List List<Integer> uniqueList = largeList.parallelStream() .distinct() .collect(Collectors.toList());但需注意,并行流适用于无状态操作,且线程安全需由数据结构保证(如
ConcurrentHashMap)。 -
分批处理:
若数据量超过内存限制,可分批读取并写入数据库或文件,利用数据库索引或外部排序去重。
去重操作的注意事项
-
对象状态一致性:
若自定义对象在去重后修改了参与equals()或hashCode()的属性,可能导致集合中出现“重复元素”,建议将对象设计为不可变类(Immutable),或避免在去重后修改关键属性。 -
null值处理:
当集合可能包含null时,需使用Objects.equals()或Optional避免空指针异常。List<String> listWithNull = Arrays.asList("A", "B", null, "A", null); List<String> uniqueList = listWithNull.stream() .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); -
性能权衡:
HashSet的添加和查询时间复杂度为O(1),适合频繁插入和查询的场景。- 对于已排序数据,可通过双指针法去重(时间复杂度O(n)),无需额外空间:
List<Integer> sortedList = Arrays.asList(1, 2, 2, 3, 4, 4, 5); List<Integer> uniqueList = new ArrayList<>(); for (int i = 0; i < sortedList.size(); i++) { if (i == 0 || !sortedList.get(i).equals(sortedList.get(i - 1))) { uniqueList.add(sortedList.get(i)); } }
Java中去除重复数据的方法需根据数据类型、业务需求和性能要求综合选择,基础场景下,HashSet和Stream.distinct()是简单高效的选择;复杂业务可通过Collectors.toMap()实现多字段去重;数据库查询优先在SQL层处理;大数据量则需关注内存优化和并行计算,无论采用何种方式,正确实现equals()和hashCode()方法始终是自定义对象去重的核心前提,通过合理选择技术和优化策略,可有效提升代码的执行效率和可维护性。
















