You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Java 8 Stream获取按所有字段去重的对象列表?

如何用Java 8 Stream按所有字段去重对象?

你遇到的问题其实很典型——distinct()方法默认依赖对象的equals()和hashCode()方法判断相等性,而默认的Object.equals()只会比较对象的引用地址,所以哪怕两个Person的字段完全一致,它们也会被当成不同对象,导致distinct()没起作用。

下面给你几种可行的解决方案:

方案一:重写Person类的equals()和hashCode()方法(推荐)

这是Java中判断对象逻辑相等的标准做法,你需要让equals()对比所有字段,hashCode()根据所有字段生成哈希值。

手动实现示例

import java.util.Objects;

public class Person {
    private String name;
    private String 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 Objects.equals(name, person.name) && Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

用Lombok简化(如果项目已引入Lombok)

加一个@EqualsAndHashCode注解就能自动生成基于所有字段的equals和hashCode方法,省去手动编写的麻烦:

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@EqualsAndHashCode
public class Person {
    private String name;
    private String age;
}

完成这一步后,你原来的代码personList.stream().distinct().collect(Collectors.toList())就能正常去重了——因为现在字段完全相同的Person会被判定为相等。

方案二:不修改Person类的情况下实现去重

如果Person是第三方类,你没法修改它的代码,可以试试这两种方式:

方式1:用TreeSet结合自定义比较器

TreeSet可以根据自定义比较器判断对象是否相等,我们可以把所有字段组合成比较逻辑:

import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

List<Person> uniquePersons = personList.stream()
    .collect(Collectors.toCollection(() -> new TreeSet<>(
        Comparator.comparing(Person::getName)
                  .thenComparing(Person::getAge)
    )))
    .stream()
    .collect(Collectors.toList());

注意:TreeSet是有序集合,且如果字段存在null值,需要额外用Comparator.nullsFirst()或nullsLast()处理。

方式2:用Filter结合HashSet跟踪已处理对象

我们可以用HashSet记录已经见过的对象,配合filter实现去重:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

Set<Person> seen = new HashSet<>();
List<Person> uniquePersons = personList.stream()
    .filter(p -> seen.add(p))
    .collect(Collectors.toList());

如果Person的equals和hashCode方法无法修改,你可以先把Person转换成包含所有字段的自定义Key对象(比如Java 16+的record),再用Key来跟踪:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

// Java 16+ 用record定义Key,低版本可以用普通类
record PersonKey(String name, String age) {}

Set<PersonKey> seenKeys = new HashSet<>();
List<Person> uniquePersons = personList.stream()
    .filter(p -> seenKeys.add(new PersonKey(p.getName(), p.getAge())))
    .collect(Collectors.toList());

总结

最推荐方案一,因为它符合Java对象相等性的设计规范,后续把对象放入Set、Map等场景也能正常工作。如果实在不能修改Person类,再考虑方案二的两种方式。

内容的提问来源于stack exchange,提问作者Nazerke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:56:15