在不使用集合的情况下过滤 java 流以获得根据属性可区分的不同对象

Filter a java stream for distinct objects distinguishable according to an attribute without using collections

我有一个包含私有 sensorID 属性的对象流(测量值)。我想过滤此流,因此我只有具有不同 sensorID 的测量值。据我了解,“不同”方法会对对象执行此操作,但不会对对象的特定属性执行此操作。此代码通过使用 Collections:

来完成这项工作
    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

并且可以这样使用

stream.filter(distinctByKey((Measurement p) -> p.getSensorId()))

测量class

public class Measurement {
    private final int sensorId;
    //more stuff
    
    public Measurement(int sensorId) {
        this.sensorId = sensorId;
        //more stuff
    }

    public int getSensorId() {
        return sensorId;
    }
}

我正在寻找的是一种无需使用集合或至少使用不可变数据类型即可完成相同操作的方法。有什么想法吗?

一种可能的替代方法是使用集合进行映射,如下例所示:

Collection<Measurement> measurementsById = stream.
        .collect(Collectors.toMap(Measurement::getSensorId, Function.identity(),
                (measurement1, measurement2) -> measurement1))
        .values()

这样做时,您正在将流元素收集到由 sensorId 键入的映射中。由于映射的键只能有一个值,因此您只需为每个键选择第一个流对象。

然后,给定结果映射,通过调用 values() 方法,您可以获得按 sensorId 分组的不同测量值列表。

@Deadpool 在他的评论中建议的解决方案很简单。 只需覆盖对象的 equals 方法即可。

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Measurement other = (Measurement) obj;
        return Objects.equals(sensorID, other.sensorID);
    }

但您还必须像这样覆盖 hashCode 方法

    @Override
    public int hashCode() {
        return Objects.hash(sensorID);
    }

这就是为什么它一开始没有用的原因。