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

Java自定义对象去重问题:HashSet与stream.distinct()失效求助

自定义对象/数组去重解决方案

问题根源

你遇到的核心问题是:HashSet和Stream的distinct()方法都依赖equals()与hashCode()的契约一致性——两个通过equals()判断相等的对象,必须返回相同的hashCode值。你只重写了PointType的equals(),但未重写hashCode(),导致JVM认为p1和p2(x、y相同的不同对象)是不同元素,因此去重失效。


1. 修复PointType类:实现完整的equals()和hashCode()

重写hashCode(),基于x、y属性生成哈希值,保证与equals()逻辑一致。同时优化equals()的严谨性(替换==为Double.compare(),避免浮点精度问题):

import java.util.Objects;

public class PointType {
    private double x;
    private double y;

    public PointType(double x, double y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) return true;
        if (!(other instanceof PointType)) return false;
        PointType point = (PointType) other;
        return Double.compare(point.x, x) == 0 && Double.compare(point.y, y) == 0;
    }

    @Override
    public int hashCode() {
        // 基于x、y生成哈希值,Objects.hash会自动处理基本类型的包装
        return Objects.hash(x, y);
    }

    // 可选:添加getter方便后续操作
    public double getX() { return x; }
    public double getY() { return y; }
}

修改后,原测试用例中的setB.size() == 2和listB.size() == 2断言都会通过:

  • HashSet会根据hashCode()分组,再用equals()去重
  • stream().distinct()的逻辑与HashSet一致,自然生效

2. double[]数组的去重方案

针对double[](每个数组代表[x,y]坐标),由于数组的equals()是引用比较,无法直接用HashSet,提供三种优雅实现:

方案A:包装为自定义类(推荐)

和PointType思路一致,将double[]包装成类,重写equals()和hashCode(),复用上面的PointType即可,或者直接用List<Double>作为键(但性能略差)。

方案B:用TreeSet+数组比较器(自动去重,会排序)

TreeSet依赖比较逻辑而非哈希值,只要比较器认为两个数组相等,就会去重:

import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;

// 示例:去重double[]数组
Set<double[]> uniquePoints = new TreeSet<>(Arrays::compare);
uniquePoints.add(new double[]{1.0, 2.0});
uniquePoints.add(new double[]{1.0, 2.0});
uniquePoints.add(new double[]{2.0, 2.0});
System.out.println(uniquePoints.size()); // 输出2

转成List的写法:

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

List<double[]> originalList = Arrays.asList(
    new double[]{1.0,2.0}, new double[]{1.0,2.0},
    new double[]{2.0,2.0}, new double[]{2.0,2.0}
);

List<double[]> uniqueList = originalList.stream()
    .collect(Collectors.toCollection(() -> new TreeSet<>(Arrays::compare)))
    .stream()
    .collect(Collectors.toList());

方案C:Stream+自定义Predicate(保留原顺序)

如果需要保持原列表的顺序,用Predicate记录已出现的元素:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

List<double[]> originalList = new ArrayList<>(Arrays.asList(
    new double[]{1.0,2.0}, new double[]{2.0,2.0},
    new double[]{1.0,2.0}, new double[]{2.0,2.0}
));

List<double[]> uniqueList = originalList.stream()
    .filter(new Predicate<double[]>() {
        private final Set<List<Double>> seen = new HashSet<>();
        @Override
        public boolean test(double[] arr) {
            return seen.add(Arrays.asList(arr[0], arr[1]));
        }
    })
    .collect(Collectors.toList());
// 输出顺序:[1.0,2.0], [2.0,2.0]

3. 额外优化:保持顺序的对象去重

如果需要保留原列表的顺序(HashSet和TreeSet会打乱顺序),同样可以用Predicate的方式:

List<PointType> uniqueOrderedList = listB.stream()
    .filter(new Predicate<PointType>() {
        private final Set<PointType> seen = new HashSet<>();
        @Override
        public boolean test(PointType p) {
            return seen.add(p);
        }
    })
    .collect(Collectors.toList());

内容的提问来源于stack exchange,提问作者Gian-Andrea Heinrich

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:21:13