当前位置:首页 > 后端开发 > 正文

java怎么去除重复数据

Java可通过 Set集合自动去重,将原数据存入 HashSet(无序)或 LinkedHashSet(保序),再转回 List

Java编程中,去除重复数据是开发过程中频繁遇到的需求,尤其在数据处理、统计分析或业务逻辑优化等场景下尤为常见,本文将从基础原理、核心实现方式、典型应用场景及性能对比四个维度展开详解,并提供可落地的代码示例与实用技巧。


核心问题定位:什么是「重复数据」?

这里的「重复」需根据具体业务定义:

简单类型:整型/浮点型的数值相等即视为重复;

对象类型:默认调用equals()方法判断内容一致性(注意需重写该方法);

复合条件:可能需要结合多个属性进行联合判重(如用户ID+操作时间)。

️ 重要前提:若未正确实现hashCode()和equals()方法,基于哈希结构的去重机制将失效!


主流解决方案及实现细节

方案1:借助Set集合特性(推荐)

子方案 特点 适用场景 注意事项
HashSet 无序,O(1)插入/查询 不关心顺序的快速去重 会打乱原始顺序
LinkedHashSet 维护插入顺序,O(1)操作 需保留顺序且允许重复出现 内存占用略高于普通HashSet
TreeSet 自动排序(自然序/定制Comparator) 需要排序后的无重复集合 字符串排序区分大小写

代码示例:

// 原始列表含重复元素 List<String> list = Arrays.asList("A", "B", "A", "C"); // 转为HashSet去重(丢失顺序) Set<String> set = new HashSet<>(list); // ["A","B","C"] // 保留顺序的去重方案 LinkedHashSet<String> linkedSet = new LinkedHashSet<>(list); // ["A","B","C"] // 转换为排序后的集合 TreeSet<String> treeSet = new TreeSet<>(list); // ["A","B","C"]

进阶用法:配合Stream API

方案2:手动遍历+辅助容器(可控性强)

适用于需要记录重复次数或执行复杂逻辑的场景:

public static <T> List<T> removeDuplicates(List<T> originalList) { Map<T, Integer> countMap = new LinkedHashMap<>(); // 保留顺序 for (T item : originalList) { countMap.put(item, countMap.getOrDefault(item, 0) + 1); } return new ArrayList<>(countMap.keySet()); }

此方法优势在于:

可扩展统计每个元素的出现次数;

通过修改Map的值类型可实现更多功能(如标记首次出现的索引);

完全控制元素的筛选逻辑。

️ 方案3:SQL层面去重(数据库交互场景)

当数据来源于数据库时,优先在SQL语句中处理:

java怎么去除重复数据 第1张

这种方式能显著减少数据传输量,提升整体性能。

️ 方案4:第三方工具类库

Apache Commons Lang提供便捷方法:

import org.apache.commons.collections4.CollectionUtils; List<String> uniqueList = CollectionUtils.removeDuplicates(originalList);

优点:一行代码解决问题;缺点:依赖外部库。


特殊场景处理指南

场景1:对象深度去重(非简单值比较)

当实体类包含多个属性时,需自定义比较逻辑:

java怎么去除重复数据 第2张

class Person { private String name; private int age; // 必须同时重写hashCode()和equals() @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Person)) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } }

此时可直接将Person对象放入HashSet进行去重。

场景2:大数据量下的内存优化

对于百万级数据:

使用BitSet代替布尔数组(仅限整型范围);

采用分块处理+磁盘缓存;

考虑并行流处理(Parallel Stream):

List<Integer> largeList = ...; // 海量数据 List<Integer> distinctList = largeList.parallelStream() .distinct() .collect(Collectors.toList());


性能对比测试(关键指标参考)

方法 时间复杂度 空间复杂度 是否保持顺序 备注
HashSet O(n) O(n) 最快但无序
LinkedHashSet O(n) O(n) 中等速度,保序
TreeSet O(nlogn) O(n) 排序耗时较长
手动遍历+HashMap O(n) O(n) 灵活度高
SQL DISTINCT O(n) O(1) 最适合数据库场景
Stream.distinct() O(n) O(n) Java8+推荐方式


常见误区警示

误用new Object().equals(obj):未重写equals的对象永远不相等;

忽略null值处理:多数集合允许单个null元素,多个null仍会被视为重复;

过度依赖简单==运算符:仅适用于基本数据类型比较;

忽视线程安全问题:ConcurrentHashMap可用于并发环境去重。


相关问答FAQs

Q1: 如果既要去重又要统计每个元素的出现次数怎么办?

A: 推荐使用Map<T, Integer>结构,遍历原集合时更新计数器,最后取Map的keySet即为去重结果,value即为出现次数,示例:

Map<String, Integer> frequencyMap = new HashMap<>(); for (String str : list) { frequencyMap.merge(str, 1, Integer::sum); } System.out.println("出现次数:" + frequencyMap);

Q2: 如何对自定义对象的多个字段进行联合去重?

A: 两种方案:①创建包含目标字段的新键对象(如复合主键类);②使用Collectors.groupingBy分组后取首条记录,推荐第一种方式,示例:

class CompositeKey { private String field1; private int field2; // 完整实现hashCode()和equals() } Set<CompositeKey> keys = new HashSet<>(); // 遍历时生成CompositeKey并加入集合

java怎么去除重复数据 第3张

0