使用Java8流API合并地图列表
我试图学习java8流,当我试图将一些函数转换为java8进行实践时。我遇到了一个问题 我很好奇如何将follow代码转换为java流格式使用Java8流API合并地图列表,java,lambda,java-8,Java,Lambda,Java 8,我试图学习java8流,当我试图将一些函数转换为java8进行实践时。我遇到了一个问题 我很好奇如何将follow代码转换为java流格式 /* * input example: * [ { "k1": { "kk1": 1, "kk2": 2}, "k2": {"kk1": 3, "kk2": 4} } { "k1": { "kk1": 10, "kk2": 20}, "k2": {"kk1": 30,
/*
* input example:
* [
{
"k1": { "kk1": 1, "kk2": 2},
"k2": {"kk1": 3, "kk2": 4}
}
{
"k1": { "kk1": 10, "kk2": 20},
"k2": {"kk1": 30, "kk2": 40}
}
]
* output:
* {
"k1": { "kk1": 11, "kk2": 22},
"k2": {"kk1": 33, "kk2": 44}
}
*
*
*/
private static Map<String, Map<String, Long>> mergeMapsValue(List<Map<String, Map<String, Long>>> valueList) {
Set<String> keys_1 = valueList.get(0).keySet();
Set<String> keys_2 = valueList.get(0).entrySet().iterator().next().getValue().keySet();
Map<String, Map<String, Long>> result = new HashMap<>();
for (String k1: keys_1) {
result.put(k1, new HashMap<>());
for (String k2: keys_2) {
long total = 0;
for (Map<String, Map<String, Long>> mmap: valueList) {
Map<String, Long> m = mmap.get(k1);
if (m != null && m.get(k2) != null) {
total += m.get(k2);
}
}
result.get(k1).put(k2, total);
}
}
return result;
}
/*
*输入示例:
* [
{
“k1”:{“kk1”:1,“kk2”:2},
“k2”:{“kk1”:3,“kk2”:4}
}
{
“k1”:{“kk1”:10,“kk2”:20},
“k2”:{“kk1”:30,“kk2”:40}
}
]
*输出:
* {
“k1”:{“kk1”:11,“kk2”:22},
“k2”:{“kk1”:33,“kk2”:44}
}
*
*
*/
私有静态映射合并映射值(列表值列表){
Set keys_1=valueList.get(0).keySet();
Set keys_2=valueList.get(0).entrySet().iterator().next().getValue().keySet();
映射结果=新的HashMap();
用于(字符串k1:键_1){
put(k1,newhashmap());
用于(字符串k2:键2){
长总计=0;
对于(地图mmap:valueList){
Map m=mmap.get(k1);
如果(m!=null&&m.get(k2)!=null){
总+=m.get(k2);
}
}
结果:get(k1)、put(k2,总计);
}
}
返回结果;
}
作为起点,转换为使用computeIfAbsent
和merge
为我们提供了以下信息:
private static <K1, K2> Map<K1, Map<K2, Long>> mergeMapsValue(List<Map<K1, Map<K2, Long>>> valueList) {
final Map<K1, Map<K2, Long>> result = new HashMap<>();
for (final Map<K1, Map<K2, Long>> map : valueList) {
for (final Map.Entry<K1, Map<K2, Long>> sub : map.entrySet()) {
for (final Map.Entry<K2, Long> subsub : sub.getValue().entrySet()) {
result.computeIfAbsent(sub.getKey(), k1 -> new HashMap<>())
.merge(subsub.getKey(), subsub.getValue(), Long::sum);
}
}
}
return result;
}
这就是我想出来的——太可怕了。问题在于,使用foreach
方法时,您对迭代的每个级别都有一个引用—这使得逻辑变得简单。使用函数的方法,你需要分别考虑每个折叠操作。
它是如何工作的
我们首先stream()
我们的列表
,给出一个流
。接下来我们将flatMap
每个元素,给出一个流
——因此我们将第一个维度展平。但我们不能进一步扁平化,因为我们需要K1
值
然后我们在K1
值上使用collect(groupingBy)
,给我们一个Map
-什么是东西
首先,我们使用映射(Entry::getValue,toList())
为我们提供映射。然后我们使用收集,然后
获取该列表
,并将其减少。请注意,这意味着我们会生成一个中间的列表
,这是一种浪费-您可以通过使用自定义的收集器来解决这个问题
为此,我们使用List.stream().reduce(a,b)
,其中a
是初始值,b
是“折叠”操作a
设置为newhashmap()
,b
取两个值:初始值或先前应用函数的结果以及列表中的当前项。因此,对于列表中的每个项目
使用Map.merge
组合值
我想说,这种方法或多或少是难以辨认的——你不可能在几个小时内破译它,更不用说几天了。这里的诀窍是正确收集内部地图。工作流程将是:
- 将地图
list
列表平面映射到地图条目流stream
- 按每个条目的键分组,对于映射到同一键的值,将两个映射合并在一起
通过合并地图来收集地图在理想情况下需要一个flatMapping
收集器,但不幸的是,Java 8中不存在这个收集器(请参阅)。对于Java8,可以使用库提供的(并在下面的代码中使用)
私有静态映射合并映射值(列表值列表){
返回valueList.stream()
.flatMap(e->e.entrySet().stream())
.collect(收集器.groupingBy(
Map.Entry::getKey,
平面映射(
e->e.getValue().entrySet().stream(),
toMap(Map.Entry::getKey,Map.Entry::getValue,Long::sum)
)
));
}
如果不使用这个方便的收集器,我们仍然可以使用等效语义构建自己的收集器:
private static Map<String, Map<String, Long>> mergeMapsValue2(List<Map<String, Map<String, Long>>> valueList) {
return valueList.stream()
.flatMap(e -> e.entrySet().stream())
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collector.of(
HashMap::new,
(r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
(r1, r2) -> { r2.forEach((k, v) -> r1.merge(k, v, Long::sum)); return r1; }
)
));
}
private静态映射mergeMapsValue2(列表值列表){
返回valueList.stream()
.flatMap(e->e.entrySet().stream())
.collect(收集器.groupingBy(
Map.Entry::getKey,
收藏(
HashMap::新建,
(r,t)->t.getValue().forEach((k,v)->r.merge(k,v,Long::sum)),
(r1,r2)->{r2.forEach((k,v)->r1.merge(k,v,Long::sum));返回r1;}
)
));
}
我从Tunaki获取了flatMap(e->e.entrySet().stream())
部分,但为收集器使用了一个较短的变量:
Map<String, Integer> merged = maps.stream()
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, Integer::sum));
Map merged=maps.stream()
.flatMap(map->map.entrySet().stream())
.collect(collector.toMap)(
Map.Entry::getKey,Map.Entry::getValue,Integer::sum));
更详细的例子:
Map<String, Integer> a = new HashMap<String, Integer>() {{
put("a", 2);
put("b", 5);
}};
Map<String, Integer> b = new HashMap<String, Integer>() {{
put("a", 7);
}};
List<Map<String, Integer>> maps = Arrays.asList(a, b);
Map<String, Integer> merged = maps.stream()
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, Integer::sum));
assert merged.get("a") == 9;
assert merged.get("b") == 5;
Map a=newhashmap(){{
付诸表决(“a”,2);
付诸表决(“b”,5);
}};
Map b=新的HashMap(){{
付诸表决(“a”,7);
}};
列表映射=数组.asList(a,b);
Map merged=maps.stream()
.flatMap(map->map.entrySet().stream())
.collect(collector.toMap)(
Map.Entry::getKey,Map.Entry::getValue,Integer::sum));
assert merged.get(“a”)==9;
assert merged.get(“b”)==5;
所以所有地图都是相同的-即在两个级别都有相同的键?这不是代码翻译服务。你需要向我们展示你已经尝试过的东西,这样我们才能告诉你你做错了什么。你应该首先重新思考你最初的方法,即在外循环中迭代什么,在内循环中迭代什么。@Holger我知道我应该先想一想,但是我完全找不到完成它的方法。我偷了你的Long::sum
-我一直忘了它的存在。我认为你的Stream
方法总体上更好,因为
Map<String, Integer> merged = maps.stream()
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, Integer::sum));
Map<String, Integer> a = new HashMap<String, Integer>() {{
put("a", 2);
put("b", 5);
}};
Map<String, Integer> b = new HashMap<String, Integer>() {{
put("a", 7);
}};
List<Map<String, Integer>> maps = Arrays.asList(a, b);
Map<String, Integer> merged = maps.stream()
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, Integer::sum));
assert merged.get("a") == 9;
assert merged.get("b") == 5;