在Java 8中使用嵌套数据结构在功能上反转映射
我有个问题,现在快把我逼疯了。我试图避免为这个贴图反转创建一个中间对象。(目标透视:我有一个嵌套数据结构的地图,我想反转和分解。所以
Map<Foo,Set<String>> fooStringMap
变成
Map<String,Foo> expandedStringFooMap
//Inverting a map is simple
private <X,Y> Map<Y,X> invertMap(Map<X,Y> source){
return source.entrySet().stream()
.collect(Collectors.toMap(Entry::getValue,Entry::getKey)
private <A,B> Map<A,B> explodeMapWithCollection(Map<? extends Collection<A>, B> collectionMap){
collectionMap.entrySet().stream()
.flatMap(x -> x.getKey().stream().collect(Collectors.toMap(Function.identity(),x.getValue())))
.collect(Collectors.toMap(Entry::getKey,Entry::getValue));
}
目前,这不起作用。我甚至不认为上面会编译,所以只考虑它是伪代码。
我用这样的一对解决了这个问题:
someMap.keySet().stream().flatMap(key->someMap.get(key).stream().map(val -> new
Pair<>(val,key))).collect(Collectors.toMap(Pair::getLeft,Pair::getRight)));
这就像一种魅力,但我(出于我自己的启发)希望避免创建中间对。我知道一定有办法做到这一点,但我似乎迷路了
# 1 楼答案
下面是一种在条目集上使用自定义^{} 的方法。有人可能会说,由于累加器中隐藏了
forEach
,这不是“完全功能”,但在某个时候,必须创建映射条目,我不确定是否有一种“优雅”的方式来使用来自Set
的流(条目值),并且仍然有可能访问条目键(它将成为新条目的值)附带说明(尽管我冒着被否决的风险,拿起棍棒进行程序性编程):你不必仅仅因为你能做到就用功能性的方式来做。当你说你“迷失在语法中”,那么
我建议保持简单。(尽管最通用的程序形式乍一看仍可能令人困惑)
# 2 楼答案
可以说,我想在这里改变这个问题的术语。谷歌优秀的Guava library有一个^{} interface 和一个^{} 子类型,有一些实现。{}的文档告诉我们:
SetMultimap
类型有一个^{Set<Map.Entry<K, V>>
结果。您可以直接在该流上stream()
和map()
反转条目,然后使用该流构建反向映射。所以像这样的事情(我肯定我做的不是最好的方式):现在,Guava似乎还没有完全跟上Java8的速度,所以您需要编写自己的
ImmutableSetMultimapCollector
(或者您想要生成的任何输出类),但这会被反复使用,所以值得This article gives some guidance.还请注意,通过使用
SetMultimap
作为结果类型,我们可以在不丢失信息的情况下反转相同值映射到两个不同键的输入。这很可能是一个加号因此,我要在这里强调两个教训:
Multimap
就是这样一种工具李># 3 楼答案
为了简单起见,我们假设您希望将
Map<Long, Set<String>>
转换为Map<String, Long>
,其中String
值跨键唯一我认为这个操作是一个带有类型为
Map<String, Long>
的累加器的左折,在Java8中它变成了一个带有累加器和组合器的约化:参见Javadoc,还有this related answer一种写作方式是这样的:
其输出如下:
注意:这与另一个answer中的想法相同,我以前没有注意到:)
# 4 楼答案
这是一个使用“reduce”的功能版本。在功能上这样做的主要缺点是,由于缺乏持久性数据结构,将导致性能不佳