在Java开发中,数据去重是一项常见且重要的操作,无论是处理集合、数组还是数据库查询结果,都可能需要去除重复元素以保证数据的唯一性和准确性,本文将详细介绍Java中多种去重方法,涵盖基础集合操作、Stream流处理、数组去重以及数据库结果去重等场景,并分析不同方法的适用场景与性能特点。

基于Set集合的天然去重特性
Set接口是Java集合框架中用于存储不重复元素的接口,其实现类如HashSet、LinkedHashSet和TreeSet天然具备去重功能,这是Java中最简单直接的去重方式。
使用HashSet去重
HashSet基于哈希表实现,插入和删除操作的平均时间复杂度为O(1),是最常用的去重工具,使用时只需将待去重集合作为构造参数传入HashSet,再转回原集合类型即可。
List<String> list = Arrays.asList("a", "b", "a", "c", "b");
Set<String> set = new HashSet<>(list);
List<String> uniqueList = new ArrayList<>(set);
需要注意的是,HashSet不保证元素的迭代顺序,如果需要保留插入顺序,应使用LinkedHashSet,它通过维护双向链表实现了元素的插入顺序存储。
使用TreeSet去重并排序
TreeSet基于红黑树实现,不仅能够去重,还能对元素进行自然排序或自定义排序。
List<Integer> numbers = Arrays.asList(3, 1, 2, 3, 2); Set<Integer> sortedSet = new TreeSet<>(numbers); List<Integer> sortedUniqueList = new ArrayList<>(sortedSet);
TreeSet的去重和排序操作时间复杂度为O(log n),适用于需要有序去重的场景,但相比HashSet性能稍低。
利用Stream API进行现代化去重
Java 8引入的Stream API为集合操作提供了函数式编程的便利,其中distinct()方法专门用于去重操作,使代码更加简洁优雅。
基本类型集合去重
对于List等集合,只需调用stream().distinct().collect()即可完成去重:

List<String> list = Arrays.asList("apple", "banana", "apple", "orange");
List<String> uniqueList = list.stream().distinct().collect(Collectors.toList());
这种方式代码简洁,且能保持原有集合的元素顺序。
自定义对象去重
当处理自定义对象时,需要重写equals()和hashCode()方法,或者使用Comparator进行自定义比较。
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);
}
}
List<Person> people = ...;
List<Person> uniquePeople = people.stream().distinct().collect(Collectors.toList());
也可以使用Collectors.toMap()或Collectors.groupingBy()结合自定义键来实现更灵活的去重逻辑:
List<Person> uniquePeople = people.stream()
.collect(Collectors.toMap(
p -> p.getName() + ":" + p.getAge(),
p -> p,
(existing, replacement) -> existing
))
.values()
.stream()
.collect(Collectors.toList());
数组去重的实现方法
Java数组没有直接的去重方法,需要先将数组转换为集合,使用上述去重方法后再转回数组。
使用Set转换
String[] array = {"a", "b", "a", "c"};
Set<String> set = new LinkedHashSet<>(Arrays.asList(array));
String[] uniqueArray = set.toArray(new String[0]);
这种方法简单高效,适用于大多数场景。
原地去重(修改原数组)
如果需要在不创建新数组的情况下去重,可以使用双指针方法:
public static int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
这种方法适用于有序数组,且时间复杂度为O(n),空间复杂度为O(1)。

数据库结果集去重
在处理数据库查询结果时,可以通过SQL语句或Java代码进行去重。
使用SQL去重
在SQL查询语句中使用DISTINCT关键字或GROUP BY子句:
SELECT DISTINCT name FROM users; SELECT name, COUNT(*) FROM users GROUP BY name;
这种方式在数据库层面完成去重,能减少数据传输量,提高性能。
在Java代码中去重
如果无法修改SQL语句,可以在获取结果集后使用上述Java集合或Stream方法去重,例如使用JDBC时:
List<String> names = new ArrayList<>();
while (resultSet.next()) {
names.add(resultSet.getString("name"));
}
List<String> uniqueNames = names.stream().distinct().collect(Collectors.toList());
性能优化与注意事项
在选择去重方法时,需要根据具体场景考虑性能因素:
- 时间复杂度:HashSet和HashMap的去重操作平均时间复杂度为O(n),TreeSet为O(n log n),对于大数据量应优先选择HashSet。
- 内存消耗:使用Set或Stream去重会创建新的集合对象,内存消耗约为原数据的1.5-2倍,对于内存敏感的场景需谨慎。
- 元素顺序:如果需要保持原始顺序,应使用LinkedHashSet或Stream的
distinct()方法,而非普通的HashSet。 - 自定义对象:确保正确实现
equals()和hashCode()方法,否则去重结果可能不符合预期。
Java中提供了多种去重方式,从基础的Set集合操作到现代化的Stream API,再到数组和数据库去重,开发者可以根据数据规模、性能要求和代码简洁性等因素选择合适的方法,对于简单场景,HashSet和Stream API是最佳选择;对于需要保持顺序的场景,LinkedHashSet或Stream的distinct()更为合适;而对于大数据量或内存敏感场景,则需考虑SQL去重或原地去重算法,掌握这些方法并能灵活运用,将有效提升Java程序的数据处理能力和代码质量。



















