>我在Streams API
遇到了一个难以解决的问题。嗯,它是可以解决的,但从我能说的,在一次通话中并不优雅。下面,以FeatureContentWeight
对象的流为例,我想按功能和内容进行分组,并获得每个功能和内容的最大权重。我在最后从Map
中获取值,因为我不需要维护Map
.问题是,我只想要包含 3 个以上项目的组。所以我想要每个功能和内容的最大权重,功能,内容对已超过给定计数。在SQL
,这只是一个简单的HAVING
条款。Streams API
它看起来并不微不足道,但我只在Streams API
呆了几天。
任何想法都值得赞赏。以下是我的方法,
List<FeatureContentWeight> nearestNeighbors = neighborPostings
.stream()
.collect(
groupingBy(
p -> FeatureContent.Create(p.getFeatureId(), p.getContentId()),
collectingAndThen(maxBy(comparingDouble(FeatureContentWeight::getWeight)),Optional::get))).values();
正如您所注意到的,不幸的是,JDK 的 Stream
API 中没有流式处理GROUP BY
操作(即使存在流式处理distinct()
操作(。 collect()
是一种终端操作,它将组和聚合收集到一个具体的Map
中。
但是,如本文所述,在 Java 8 Streams 中展示 SQL 子句及其等效项,您可以重新流式传输Map.entrySet()
并对其执行进一步的操作。
应用于您的代码(我在这里做一些假设(:
Map<FeatureContentWeight, Double> nearestNeighbors = neighborPostings
.stream()
// GROUP BY featureId, contentId
.collect(
groupingBy(
p -> FeatureContent.Create(p.getFeatureId(), p.getContentId())
)
)
// HAVING count(*) >= 3
.entrySet()
.stream()
.filter(e -> e.getValue().size() >= 3)
// SELECT grp, MAX(weight)
.map(e -> e.getValue().stream().collect(
maxBy(comparingDouble(w -> w.getWeight))
));
从您的描述来看,您似乎要过滤作为分组结果的地图。因此,您可以对分组结果应用toMap
收集器,然后过滤其values()
,以便仅保留长度为 3 或更大的收集器。您可能也可以跳过地图创建并使用partitioningBy
收集器,但这可能会更尴尬。
最后,对于转换地图,我发现 Guava 的辅助函数(如 Maps.filterValues()
(有时提供比 Java 8 更短、更易读的语法(流语法对于列表来说很好,但对于地图来说有时会变得很糟糕(。如果你使用的是Java 8,你可以在Guava中使用闭包,所以你可以写一些类似的东西:
Map<A,B> unfiltered = <Java 8 grouping>
return Maps.filterValues(unfiltered, list -> list.size() > 3);
resultMap.values().removeIf(lst -> lst.size() < 3)
只需从结果中删除您不需要的所有内容