过滤java流中可根据属性区分的不同对象,而不使用集合
我有一个包含私有sensorID属性的对象流(测量)。我想过滤这个流,所以我只有不同传感器的测量。据我所知,“distinct”方法将对对象执行此操作,但不会对对象的特定属性执行此操作。此代码通过使用集合执行此任务:过滤java流中可根据属性区分的不同对象,而不使用集合,java,java-stream,distinct-values,Java,Java Stream,Distinct Values,我有一个包含私有sensorID属性的对象流(测量)。我想过滤这个流,所以我只有不同传感器的测量。据我所知,“distinct”方法将对对象执行此操作,但不会对对象的特定属性执行此操作。此代码通过使用集合执行此任务: public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) { Map<Object, Boolean>
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;
}
对于测量类
public class Measurement {
private final int sensorId;
//more stuff
public Measurement(int sensorId) {
this.sensorId = sensorId;
//more stuff
}
public int getSensorId() {
return sensorId;
}
}
我要寻找的是一种不使用集合或至少使用不可变数据类型来做同样事情的方法。任何想法?一种可能的替代方法是使用集合进行映射,如以下示例所示:
Collection measurementsById=stream。
.collect(Collectors.toMap(Measurement::getSensorId,Function.identity(),
(测量1,测量2)->测量1)
.values()
这样做时,您将流元素收集到由sensorId
键控的地图中。因为一个映射对于一个键只能有一个值,所以只需为每个键选择第一个流对象
然后,给定结果图,通过调用
values()
方法,您可以获得按sensorId
分组的不同测量值列表。一种可能的替代方法是使用集合进行映射,如以下示例:
Collection measurementsById=stream。
.collect(Collectors.toMap(Measurement::getSensorId,Function.identity(),
(测量1,测量2)->测量1)
.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方法
stream.filter(distinctByKey((Measurement p) -> p.getSensorId()))
@Override
public int hashCode() {
return Objects.hash(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方法
stream.filter(distinctByKey((Measurement p) -> p.getSensorId()))
@Override
public int hashCode() {
return Objects.hash(sensorID);
}
这就是为什么它一开始不起作用。为什么不在
测量上覆盖equals
方法?并使用stream.distinct()
?事实上,我尝试过这个,但它对我不起作用。也许我犯了一个错误?你可能错过了一些东西,但你可以把它作为参考@Deadpool Yes,这很有帮助-它实际上是正确的解决方案,我只需要重写hashcode方法就可以了。Thanks@DanielWagnerdistinct
的源代码也有一个非常类似的实现,使用ConcurrentHashMap
。为什么不在测量上覆盖equals
方法呢?并使用stream.distinct()
?事实上,我尝试过这个,但它对我不起作用。也许我犯了一个错误?你可能错过了一些东西,但你可以把它作为参考@Deadpool Yes,这很有帮助-它实际上是正确的解决方案,我只需要重写hashcode方法就可以了。Thanks@DanielWagnerdistinct
的源代码也有一个非常类似的实现,它使用了ConcurrentHashMap
。这是一个带有集合的解决方案。我正在寻找没有集合的解决方案。@DanielWagner您认为如何实现distinct()
?这是一个有集合的解决方案。我正在寻找没有集合的解决方案。@DanielWagner你认为如何实现distinct()
?我不认为在对象有很多其他属性时基于一个属性实现相等是个好主意。您正在调整类的总体设计,以适应非常具体的单个任务。当你不想让所有具有相同传感器ID的对象都相同时,要准备好在其他场景中出现“奇怪行为”。我同意@Holger在这里的评论。我不认为当对象有很多其他属性时,基于一个属性实现相等是一个好主意。您正在调整类的总体设计,以适应非常具体的单个任务。当你不想让所有具有相同传感器ID的对象都相同时,要准备好在其他场景中出现“怪异行为”。我同意@Holger的评论。