在GroupingBy中等效的Java 8 Streams API HAVING子句?

时间:2014-11-05 15:52:40

标签: java java-8 stream

我在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();

3 个答案:

答案 0 :(得分:2)

根据您的描述,您似乎想要过滤作为分组结果的地图。因此,您可以对分组结果应用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);

答案 1 :(得分:1)

正如您已经注意到的那样,遗憾的是,JDK的GROUP BY API中没有流媒体Stream操作(即使有流媒体distinct()操作)。 collect()是一个终端操作,它将组和聚合收集到具体的Map

但是,正如本article showing SQL clauses and their equivalents in Java 8 Streams中所述,您可以重新传输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))
    ));

答案 2 :(得分:1)

resultMap.values().removeIf(lst -> lst.size() < 3)

从结果中删除不需要的所有内容