Java stream collect使用多个键进行映射

Java stream collect使用多个键进行映射,java,key,java-stream,collect,Java,Key,Java Stream,Collect,我以为我在Java8流方面做得很好,但是 我有一个Foo界面: public interface Foo { String getKey(); Stream<Bar> bars(); } 公共接口Foo{ 字符串getKey(); 流坝(); } 我知道我可以使用以下键将流收集到地图: Map<String, Foo> foosByKey = fooStream.collect( Collectors.toMap(Foo::getKey, Functi

我以为我在Java8流方面做得很好,但是

我有一个
Foo
界面:

public interface Foo {
  String getKey();
  Stream<Bar> bars();
}
公共接口Foo{
字符串getKey();
流坝();
}
我知道我可以使用以下键将
收集到
地图

Map<String, Foo> foosByKey = fooStream.collect(
    Collectors.toMap(Foo::getKey, Function.identity()));
Map foosByKey=fooStream.collect(
toMap(Foo::getKey,Function.identity());
但是如果我想将它们收集到
地图中
,该怎么办?换句话说,对于steam中的每个
Foo
,我想将该
Foo
放在映射中,映射到
Foo.Bar()返回的每个
Bar
实例。从何处开始?

如建议,您需要从每个
Foo
中提取
值,并创建成对的值。一旦有了这些对,就可以将它们收集到
地图中
。比如说,

Map<Bar, Foo> map = fooStream.flatMap(foo -> foo.bars().map(bar -> new SimpleEntry<>(bar, foo)))
            .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 
Map-Map=fooStream.flatMap(foo->foo.bar().Map(bar->newsimplentry(bar,foo)))
.collect(Collectors.toMap(条目::getKey,条目::getValue));

我们在这里使用
SimpleEntry
,因为它是可用的(Java没有更简单的
类型)。您可以编写自己的更具体的收集器。

您可以为此定义一个新的收集器。一个简单的实现(总是创建ArrayList的HashMap;没有下游支持)可以是:

public static <T, K>
Collector<T, ?, Map<K, List<T>>> multiGroupingBy(
        Function<? super T, Collection<? extends K>> multiClassifier) {
    return Collector.of(
            HashMap::new,
            (map, entry) -> {
                multiClassifier.apply(entry)
                        .forEach(
                                key -> map
                                        .computeIfAbsent(key,
                                                __ -> new ArrayList<>())
                                        .add(entry));
            },
            (map1, map2) -> {
                map2.forEach(
                        (key, list) -> map1
                                .computeIfAbsent(key,
                                        __ -> new ArrayList<>())
                                .addAll(list));
                return map1;
            });
}

不,你把它倒过来了。
Map
意味着
Bar
Foo
实例之间的一对一映射。考虑<代码>地图>代码>或代码>地图<代码>,其中一个人可以有多张信用卡,一本书可以有多个ISBN。这里没什么复杂的。我确实理解它的意思,但我不想在有点辅助性的问题上挂断电话。映射是否双向取决于图形是否具有方向性。Java
Map
是单向的,所以你不能映射回去——这就是为什么番石榴有一个
BiMap
。我想你是想说这种关系是一对一的——在这种情况下,这种关系是多对一的(
Bar
-
Foo
)。但是,这又一次有点离题了——关于啤酒的争论。总结是,每个
Foo
可以有许多
Bar
,我想将
Bar
映射到
Foo
。干杯事实上,我们可以用这样一句话来结束:每个
Foo
可以有许多
Bar
,并且所有
Bar
都是不同的,我认为包含了一个适当的解决方案。是否有什么东西阻止你接受他的答案,我们应该解决?我希望有一些更优雅和简洁的东西;没有明确的中间人。我会再坚持一段时间,看看是否有人想出了什么窍门。我尝试了一个专门的收集器,然后我意识到这不仅可读性较差,最终它只是用捕获lambda实例的隐式实例化取代了
Map.Entry
实例的显式实例化,因此根本没有任何改进。
SimpleEntry
的使用很酷——我没有意识到Java至少有类似于
对的东西。总的来说,虽然我希望有一个更紧凑的东西,但这可能是我们能得到的最好的。我将把它放一段时间,看看是否还有其他选择。Java9将增加使用
Map.entry(bar,foo)
以更简洁的方式构造基于值的、不可变的条目的可能性。
fooStream.collect(multiGroupingBy(Foo::bars));