JAVA8 - 使用lambda进行分组

4

我有一个类似于这样的结构的集合:

@Entity
public class RRR{

    private Map<XClas, YClas> xySets;

}

并且XClass有一个名为ZZZ的字段。

我的问题是: 我想使用lambda进行聚合,以获取一个Map<ZZZ,List<RRR>>。是否可能?现在我卡住了:

Map xxx = rrrList.stream().collect(
                Collectors.groupingBy(x->x.xySets().entrySet().stream().collect(
                        Collectors.groupingBy(y->y.getKey().getZZZ()))));

但它是 Map<Map<ZZZ, List<XClas>>, List<RRR>>,所以这不是我要找的 :)

现在只是为了让它能够工作,我使用了两个嵌套循环进行聚合,但如果您能帮助我使用lambda实现,那就太好了。

编辑

我按要求发布了我现在得到的内容。我已经离开了嵌套循环,并设法完成了这一点:

Map<ZZZ, List<RRR>> temp;
rrrList.stream().forEach(x -> x.getxySetsAsList().stream().forEach(z -> {
            if (temp.containsKey(z.getKey().getZZZ())){
                List<RRR> uuu = new LinkedList<>(temp.get(z.getKey().getZZZ()));
                uuu.add(x);
                temp.put(z.getKey().getZZZ(), uuu);
            } else {
                temp.put(z.getKey().getZZZ(), Collections.singletonList(x));
            }
        }));

感谢您的提前帮助。

什么是“assignments”?一个“RRR”的“Collection”吗?您能提供带有“for”循环的工作版本吗? - Djon
现在已经修正了 - 对此感到抱歉。 - Lukas Novicky
getxySetsAsList(),是获取键或值的列表吗?看起来像是一个 Entry 的列表,但你只使用了键。 - Djon
我一直在试图想出一个聪明的方法来做这件事,但是越想,我越相信你应该坚持使用一个普通的旧版Java8嵌套循环。 - tobias_k
我也在努力,但命名规范对我来说太多了。@LuciusSnow 你试过使用Collectors.reducing()吗? - Djon
显示剩余2条评论
3个回答

5
像这样吗?:
    Map<ZZZ, List<RRR>> map = new HashMap<>();

    list.stream().forEach(rrr -> {
        rrr.xySets.keySet().stream().forEach(xclas -> {
            if (!map.containsKey(xclas.zzz))
                map.put(xclas.zzz, new ArrayList<RRR>());
            map.get(xclas.zzz).add(rrr);
        });
    });

+1,因为它有效,但与使用传统的双重循环相比,有什么好处吗? - tobias_k
1
Lambda表达式应该更快、更高效。 - Lukas Novicky
很抱歉,在其他答案之后,有一个更好的答案,只使用lambda进行聚合。而且,最重要的是,另一种解决方案比这个更快。不过,还是谢谢你的时间 :) - Lukas Novicky

3
另一种方法是这样的:
Map<Z, List<R>> map = rs.stream()
        .map(r -> r.xys.keySet()
            .stream()
            .collect(Collectors.<X, Z, R>toMap(x -> x.z, x -> r, (a, b) -> a)))
        .map(Map::entrySet)
        .flatMap(Collection::stream)
        .collect(Collectors.groupingBy(Entry::getKey,
                Collectors.mapping(Entry::getValue, Collectors.toList())));

Collectors.<X, Z, R>toMap(x -> x.z, x -> r, (a, b) -> a)) 这段代码是什么意思?它无法编译,我也很难理解它应该如何工作,因此我无法根据自己的需求进行修复或自定义 :( - Lukas Novicky
1
@LuciusSnow 我在编写时稍微修改了你的名称,XXClasZZZZRRRRx.z 将是 x.getZZZ(),而 r.xys.keySet() 则是 r.xySets.keySet(),这解决了你的问题吗? - Alex - GlassEditor.com
现在它运行得很好:) 抱歉,我是Lambda的新手。我知道它非常有用,所以我正在尽力使用它,但有时候它仍然有点难以阅读 :) 谢谢 - Lukas Novicky

2

我尝试了一些方法,找到了以下解决方案,并在此处发布它作为另一个示例:

rrrList.stream().map(x -> x.xySets).map(Map::entrySet).flatMap(x -> x.stream())
    .collect(Collectors.groupingBy(x -> x.getKey().getZZZ(), 
        Collectors.mapping(Entry::getValue, Collectors.toList())));

第一行也可以写成rrrList.stream().flatMap(x -> x.xySets.entrySet().stream()),这样更易读。以下是一个自包含的示例代码,供想要自己尝试的人使用:
public static void main(String[] args) {
    List<RRR> rrrList = Arrays.asList(new RRR(), new RRR(), new RRR());
    System.out.println(rrrList);
    Stream<Entry<XClas, YClas>> sf = rrrList.stream().map(x -> x.xySets).map(Map::entrySet).flatMap(x -> x.stream());
    Map<ZZZ, List<YClas>> res = sf.collect(Collectors.groupingBy(x -> x.getKey().getZZZ(), Collectors.mapping(Entry::getValue, Collectors.toList())));
    System.out.println(res);
}

public static class RRR {
    static XClas shared = new XClas();
    private Map<XClas, YClas> xySets = new HashMap<>();
    RRR() { xySets.put(shared, new YClas()); xySets.put(new XClas(), new YClas()); }
    static int s = 0; int n = s++; 
    public String toString() { return "RRR" + n + "(" + xySets + ")"; }
}
public static class XClas {
    private ZZZ zzz = new ZZZ();
    public ZZZ getZZZ() { return zzz; }
    public String toString() { return "XClas(" + zzz + ")"; } 
    public boolean equals(Object o) { return (o instanceof XClas) && ((XClas)o).zzz.equals(zzz); }
    public int hashCode() { return zzz.hashCode(); }
}
public static class YClas {
    static int s = 0; int n = s++; 
    public String toString() { return "YClas" + n; }
}
public static class ZZZ { 
    static int s = 0; int n = s++ / 2;
    public String toString() { return "ZZZ" + n; }
    public boolean equals(Object o) { return (o instanceof ZZZ) && ((ZZZ)o).n == n; }
    public int hashCode() { return n; }
}

代码无法编译 :( 这一行出了问题: Map<ZZZ, List<YClas>> res = sf.collect(Collectors.groupingBy(x -> x.getKey().getZZZ(), Collectors.mapping(Entry::getValue, Collectors.toList()))); - Lukas Novicky
最后一个收集器出现错误:Collectors.mapping(Entry::getValue, Collectors.toList() 链接 - Lukas Novicky
好的 - 你的独立示例无法编译,但我决定在我的项目中尝试一下。有趣的是 - JetBrains IntelliJ将我提到的那一行标记为错误,但整个项目可以编译(!),而且更好的是 - 它还能正常工作 :) 我不知道为什么,带有标记错误的代码能够正常工作,但它确实可以 :) 而且速度非常快 :) - Lukas Novicky
我很高兴它最终能为你工作。我这里使用的是Oracle JDK 1.8.0_11。Eclipse Luna(和NetBeans 8)没有指示任何错误。 - halfbit
更新我的IDE有助于错误信息 - IntelliJ 13.1.4已经完全支持Java8 - 13.0.4只有部分支持。这就是我自己解决问题时遇到困难的原因:/ 真糟糕...还是非常感谢您的时间 :) - Lukas Novicky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接