Java案例如何去除集合重复值?

wen java案例 12

本文目录导读:

Java案例如何去除集合重复值?

  1. 目录导读
  2. 方法一:利用HashSet特性去重(最常用)
  3. 方法二:使用Java 8 Stream API的distinct()
  4. 方法三:通过LinkedHashSet保持原顺序去重
  5. 方法四:借助TreeSet实现排序去重
  6. 方法五:手动遍历与Map去重(自定义场景)
  7. 常见问答(Q&A)
  8. 性能对比与选择建议

Java案例精讲:如何高效去除集合中的重复值?5种实战方法详解

目录导读

  • 集合重复数据问题的普遍性与处理价值
  • 利用HashSet特性去重(最常用)
  • 使用Java 8 Stream API的distinct()
  • 通过LinkedHashSet保持原顺序去重
  • 借助TreeSet实现排序去重
  • 手动遍历与Map去重(自定义场景)
  • 常见问答(Q&A)
  • 性能对比与选择建议

在实际项目开发中,集合(List、Set等)中出现重复值是非常常见的问题,从数据库查询到的数据、用户输入列表、文件读取内容等场景,数据冗余不仅浪费内存,还可能影响业务逻辑的正确性,掌握多种去重方案是Java开发者的基本功。

本文将通过5个真实案例,详细讲解不同场景下如何高效去除集合重复值,涵盖从基础到高级(Java 8+)的方法,并附上性能对比和常见问题解答。


利用HashSet特性去重(最常用)

案例场景

假设有一个List<String>包含重复的姓名,需要去除重复元素。

实现代码

import java.util.*;
public class RemoveDuplicates1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("张三", "李四", "张三", "王五", "李四"));
        // 将List转成HashSet,自动去重
        Set<String> set = new HashSet<>(list);
        // 如果需要返回List,再转回来
        List<String> result = new ArrayList<>(set);
        System.out.println(result); // 输出:[李四, 张三, 王五](无序)
    }
}

原理分析

HashSet底层基于哈希表,利用hashCode()equals()方法判断元素是否重复,插入时,若元素已存在,则拒绝添加,整个过程时间复杂度为O(n),效率极高。

注意事项

  • 不保证原顺序,如果依赖顺序请使用方法三。
  • 自定义对象必须重写equals()hashCode()

使用Java 8 Stream API的distinct()

案例场景

同样处理上述姓名列表,但希望代码更简洁(Java 8+环境)。

实现代码

import java.util.*;
import java.util.stream.Collectors;
public class RemoveDuplicates2 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("张三", "李四", "张三", "王五", "李四");
        List<String> result = list.stream()
                                  .distinct()
                                  .collect(Collectors.toList());
        System.out.println(result); // 输出:[张三, 李四, 王五](保留首次出现顺序)
    }
}

原理分析

distinct()是Stream API中的中间操作,内部通过LinkedHashSet实现去重,因此能保留原顺序,它是处理有序去重的首选方案之一。

优势

  • 函数式编程,代码优雅。
  • 支持链式操作(如过滤、映射后去重)。

通过LinkedHashSet保持原顺序去重

案例场景

需要去重,同时必须保持元素插入的原始顺序。

实现代码

import java.util.*;
public class RemoveDuplicates3 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("张三", "李四", "张三", "王五", "李四");
        Set<String> set = new LinkedHashSet<>(list);
        List<String> result = new ArrayList<>(set);
        System.out.println(result); // 输出:[张三, 李四, 王五](顺序保留)
    }
}

原理分析

LinkedHashSet继承自HashSet,但内部维护了一个双向链表,用于记录元素的插入顺序,因此去重同时能稳定保留原顺序,适合对顺序敏感的UI列表、数据模板等场景。


借助TreeSet实现排序去重

案例场景

去重的同时希望元素自然排序(如数字从小到大,字符串字典序)。

实现代码

import java.util.*;
public class RemoveDuplicates4 {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(3, 1, 2, 1, 4, 3, 5);
        Set<Integer> sortedSet = new TreeSet<>(numbers);
        System.out.println(sortedSet); // 输出:[1, 2, 3, 4, 5](排序且去重)
    }
}

原理分析

TreeSet底层是红黑树(自平衡二叉查找树),元素按自然顺序或自定义Comparator排序,插入和查找复杂度为O(log n),适合需要有序结果集的场景。

自定义排序

Set<Person> set = new TreeSet<>((a, b) -> a.age - b.age);

手动遍历与Map去重(自定义场景)

案例场景

需要根据对象的某个字段去重(如按用户ID去重,保留最新一条)。

实现代码

import java.util.*;
class User {
    int id;
    String name;
    User(int id, String name) { this.id = id; this.name = name; }
    // getter 省略
}
public class RemoveDuplicates5 {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User(1, "张三"),
            new User(2, "李四"),
            new User(1, "张三(重复)") // ID相同,视为重复
        );
        Map<Integer, User> map = new HashMap<>();
        for (User u : users) {
            map.putIfAbsent(u.id, u); // 仅当Key不存在时放入
        }
        List<User> result = new ArrayList<>(map.values());
        System.out.println(result.size()); // 输出:2
    }
}

原理分析

利用Map的Key唯一性,可自由控制保留策略(如覆盖、保留第一条等),适用于复杂业务去重(如按多个字段组合去重)。


常见问答(Q&A)

Q1:HashSet和LinkedHashSet哪个去重效率更高?
A:HashSet效率略高(O(1)),因为它不需要维护链表,但如果需要保留顺序,使用LinkedHashSet,其额外开销很小。

Q2:Stream distinct()会改变原集合吗?
A:不会,Stream操作返回新集合,原集合保持不变,如果需要修改原变量,需重新赋值。

Q3:自定义对象去重必须重写equals和hashCode吗?
A:是的,否则Object类的equals默认比较内存地址,会导致对象即使内容相同也被看作不同元素。

Q4:TreeSet去重时,比较器返回0为什么会被视为重复?
A:TreeSet认为compareTo()返回0表示两个元素“相等”,因此不会插入,注意这与equals()是否重写无关。

Q5:对于大数据量(百万级)去重,哪种方法推荐?
A:推荐HashSet(无序)或Stream.distinct()(有序),因为它们是O(n)复杂度,避免使用TreeSet(O(n log n))在大数据量下性能下降。


性能对比与选择建议

方法 时间复杂度 是否保留顺序 适用场景
HashSet O(n) 对顺序无要求,追求速度
Stream distinct() O(n) Java 8+,代码简洁
LinkedHashSet O(n) 需要保留插入顺序
TreeSet O(n log n) 是(排序) 需要排序结果
Map手动遍历 O(n) 视实现而定 复杂字段去重

最终建议

  • 简单去重:List → Set → List 即可。
  • 保持顺序:用Stream.distinct()LinkedHashSet
  • 自定义规则:使用Map+putIfAbsentStream+Collectors.toMap

抱歉,评论功能暂时关闭!