使用 Java 8 Lambda 表达式合并 Map 流
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31488339/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Merging Map streams using Java 8 Lambda Expression
提问by viktor
I am new to lambda expression and was trying to use it in my assignment.
我是 lambda 表达式的新手,并试图在我的作业中使用它。
The problem:
问题:
I have two maps m1
and m2
of type Map<Integer, String>
, which has to be merged into a single map
Map<Integer, List<String>>
, where values of same keys in both the maps are collected into a List and put into a new Map.
我有两个 mapm1
和m2
type Map<Integer, String>
,它们必须合并到一个 map 中
Map<Integer, List<String>>
,其中两个 map 中相同键的值被收集到一个 List 中并放入一个新的 Map 中。
Solution based on what I explored:
基于我探索的解决方案:
Map<Integer, List<String>> collated =
Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()).collect(
Collectors.toMap(Entry::getKey,
Entry::getValue, (a, b) -> {
List<String> merged = new ArrayList<String>(a);
merged.addAll(b);
return merged;
}));
But, this solution expects source List
to be Map<Integer, List<String>>
as the merge function in toMap expects operands and result to be of the same type.
但是,这个解决方案期望 sourceList
是Map<Integer, List<String>>
因为 toMap 中的合并函数期望操作数和结果是相同的类型。
I don't want to change the source collection. Please provide your inputs on achieving this using lambda expression.
我不想更改源集合。请提供有关使用 lambda 表达式实现此目标的输入。
采纳答案by Eran
I can't test it right now, but I think all you need it to change the mapping of the value from Entry::getValue to a List that contains that value :
我现在不能测试它,但我认为你需要它来将值的映射从 Entry::getValue 更改为包含该值的 List :
Map<Integer, List<String>> collated =
Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey,
e -> {
List<String> v = new ArrayList<String>();
v.add(e.getValue());
return v;
},
(a, b) -> {
List<String> merged = new ArrayList<String>(a);
merged.addAll(b);
return merged;
}));
EDIT: The idea was correct. The syntax wasn't. Current syntax works, though a bit ugly. There must be a shorter way to write it.
编辑:这个想法是正确的。语法不是。当前的语法有效,虽然有点难看。必须有更短的写法。
You can also replace e -> {..}
with e -> new ArrayList<String>(Arrays.asList(new String[]{e.getValue()}))
.
您也可以替换e -> {..}
为e -> new ArrayList<String>(Arrays.asList(new String[]{e.getValue()}))
.
or with e -> Stream.of(e.getValue()).collect(Collectors.toList())
或与 e -> Stream.of(e.getValue()).collect(Collectors.toList())
EDIT :
编辑 :
Or you can do it with groupingBy
:
或者你可以这样做groupingBy
:
Map<Integer, List<String>> collated =
Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
回答by Misha
That's a job for groupingBy
collector:
这是groupingBy
收藏家的工作:
Stream.of(m1,m2)
.flatMap(m->m.entrySet().stream())
.collect(groupingBy(
Map.Entry::getKey,
mapping(Map.Entry::getValue, toList())
));
回答by Tagir Valeev
Misha's solution is the best if you want pure Java-8 solution. If you don't mind using third-party libraries, it would be a little shorter using my StreamEx.
如果您想要纯 Java-8 解决方案,Misha 的解决方案是最好的。如果您不介意使用第三方库,那么使用我的StreamEx会短一些。
Map<Integer, List<String>> map = StreamEx.of(m1, m2)
.flatMapToEntry(Function.identity())
.grouping();
Internally it's the same as in Misha's solution, just syntactic sugar.
在内部,它与 Misha 的解决方案相同,只是语法糖。
回答by Jeffrey
This seems like a great opportunity to use Guava's Multimap
.
这似乎是使用 Guava 的Multimap
.
ListMultimap<Integer, String> collated = ArrayListMultimap.create();
collated.putAll(Multimaps.forMap(m1));
collated.putAll(Multimaps.forMap(m2));
And if you really need a Map<Integer, List<String>>
:
如果你真的需要一个Map<Integer, List<String>>
:
Map<Integer, List<String>> mapCollated = Multimaps.asMap(collated);