我想要的建议是通过某个内部对象的字段过滤列表。因此我在互联网上找到一种方法,如下所示:
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
那么,我这样用
list.stream().filter(LambdaUtils.distinctByKey(o -> o.getLogisticsNumber() == null ? o.getId() : o.getLogisticsNumber())).collect(Collectors.toList());
它工作正常,但我想尝试不同的东西,所以我改变代码不使用函数,而不是代码。像这样:
list.stream().filter(
x -> {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
Function<DeliveryOrderItemMaterialLogisticInfo ,Object> keyExtractor = o -> o.getLogisticsNumber() == null ? o.getId() : o.getLogisticsNumber();
return seen.putIfAbsent(keyExtractor.apply(x), Boolean.TRUE) == null;
}
).collect(Collectors.toList());
那么结果就是错误。
为什么?这两者有什么不同?
在您的第二个代码版本中:
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
地图Map<> seen
是为每个元素x
初始化的,因此你的过滤器总是返回true
,这将让所有元素直通。
将Map<> seen
声明移动到lambda外部将更正您的使用情况。
在第一个代码版本中,Map<> seen
仅初始化为一个。它返回的只是一个谓词:
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
当您将此谓词传递给流时,它不会创建新的Map
,而是使用之前声明的引用Map<> seen
。