Java List 去重

Java List 去重有多种实现方式,选择取决于 是否需保留原顺序去重依据 (完整对象 vs 指定字段)及 Java 版本。以下是生产环境最常用、最安全的方案:

🎯 一句话推荐

场景 推荐方案 是否保序 时间复杂度
equals() 去重 stream().distinct() ✅ 是 O(n)
按指定字段去重 filter + Set.add() 技巧 ✅ 是 O(n)
极致性能(不保序) new HashSet<>(list) ❌ 否 O(n)

📦 1. 按对象整体去重(保留原顺序)

依赖 equals()hashCode(),适合已正确重写这两个方法的对象。

复制代码
复制代码
List<String> deduped = list.stream()
    .distinct() // 基于 equals() 判断,自动保序
    .collect(Collectors.toList());

// 或更高效的底层写法(少一次 Stream 开销)
List<String> deduped = new ArrayList<>(new LinkedHashSet<>(list));

🔑 2. 按指定字段去重(实战高频)

需求 :根据 User.getId()User.getName() 去重,且保留第一次出现的元素。

复制代码
复制代码
// ✅ 推荐:利用 Set.add() 返回值特性,简洁高效
Set<Integer> seenIds = new HashSet<>();
List<User> deduped = list.stream()
    .filter(user -> seenIds.add(user.getId())) // add 返回 false 表示已存在
    .collect(Collectors.toList());

// ⚠️ 注意:若字段可能为 null,改用 Objects.hash() 或判空逻辑
Set<Object> seen = new HashSet<>();
List<User> deduped = list.stream()
    .filter(u -> seen.add(u.getName() == null ? "NULL_KEY" : u.getName()))
    .collect(Collectors.toList());

⚡ 3. 不关心顺序(追求极致性能)

放弃顺序可换用 HashSet,内存和速度最优。

复制代码
复制代码
List<User> deduped = new ArrayList<>(new HashSet<>(list));

⚠️ 核心避坑指南

  1. 必须重写 equals() & hashCode()
    自定义对象若未重写,distinct()Set 会按内存地址 比较,导致去重失败。

    复制代码
    复制代码
    @Data // Lombok 自动生成
    public class User {
        private Integer id;
        private String name;
    }
  2. 不要用 list.contains() 循环去重
    contains() 底层是线性查找,嵌套循环会导致 O(n²) 性能雪崩。

    复制代码
    复制代码
    // ❌ 生产环境禁止
    for (int i = 0; i < list.size(); i++) {
        for (int j = i + 1; j < list.size(); j++) { ... }
    }
  3. 原列表不可变时,去重会抛异常
    Arrays.asList()List.of() 返回的是只读列表,需先转 ArrayList

    复制代码
    复制代码
    List<String> mutable = new ArrayList<>(Arrays.asList("A", "B", "A"));
    List<String> deduped = new ArrayList<>(new LinkedHashSet<>(mutable));

📊 性能与适用场景对比

方案 保序 依赖 性能 适用场景
stream().distinct() equals/hashCode ⭐⭐⭐ 通用、代码简洁
LinkedHashSet equals/hashCode ⭐⭐⭐⭐ 大数据量、追求效率
filter + Set.add() 指定字段 ⭐⭐⭐⭐ 按业务字段去重
HashSet equals/hashCode ⭐⭐⭐⭐⭐ 顺序无关的缓存/集合处理

💡 调试技巧 :去重前打印 list.stream().map(User::getId).distinct().count() 可快速验证去重数量是否符合预期。

相关推荐
大模型码小白15 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司16 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地17 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
莫逸风19 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
z1234567898620 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
王维同学20 小时前
[原创][Windows C++]LSA 认证、安全与通知包的注册表枚举
c++·windows·安全
yaoxin52112321 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python
YCOSA202521 小时前
雨晨 Windows 11 IOT 企业版 LTSC 24H2 轻装 2合1 26100.8968
windows·物联网
做个文艺程序员21 小时前
Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维
java·grafana·prometheus
汤姆yu21 小时前
CodeGeeX 4完整安装与实操使用全指南
人工智能·windows·ai·智能体·视频模型