[英]Convert type X to Y in Map<K, Map<V, X>> using Java Stream API
I want to convert inner map from map of maps. 我想从地图地图转换内部地图。
Old map: Map<String, Map<LocalDate, Integer>>
Integer means seconds 旧地图: Map<String, Map<LocalDate, Integer>>
整数表示秒
New map: Map<String, Map<LocalDate, Duration>>
新地图: Map<String, Map<LocalDate, Duration>>
I have tried created new inner map, but got an error 我试过创建了新的内部地图,但是出了错误
Error: java: no suitable method found for
putAll(java.util.stream.Stream<java.lang.Object>)
methodjava.util.Map.putAll(java.util.Map<? extends java.time.LocalDate,? extends java.time.Duration>)
is not applicable 错误:java:找不到适合putAll(java.util.stream.Stream<java.lang.Object>)
方法putAll(java.util.stream.Stream<java.lang.Object>)
方法java.util.Map.putAll(java.util.Map<? extends java.time.LocalDate,? extends java.time.Duration>)
不适用
oldMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> new HashMap<LocalDate, Duration>() {{
putAll(
e.getValue().entrySet().stream()
.map(x -> new HashMap.SimpleEntry<LocalDate, Duration>
(x.getKey(), Duration.ofSeconds(x.getValue())))
);
}}
));
If you want compact code, you may use 如果你想要紧凑的代码,你可以使用
Map<String, Map<LocalDate, Duration>> newMap = new HashMap<>();
oldMap.forEach((s,o) -> o.forEach((d, i) ->
newMap.computeIfAbsent(s, x->new HashMap<>()).put(d, Duration.ofSeconds(i))));
If you want to avoid unnecessary hash operations, you may expand it a bit 如果要避免不必要的哈希操作,可以稍微扩展一下
Map<String, Map<LocalDate, Duration>> newMap = new HashMap<>();
oldMap.forEach((s,o) -> {
Map<LocalDate, Duration> n = new HashMap<>();
newMap.put(s, n);
o.forEach((d, i) -> n.put(d, Duration.ofSeconds(i)));
});
Quick and clean 快速而干净
HashMap<String, HashMap<LocalDate, Duration>> newMap = new HashMap<>();
oldHashMap.forEach((key, innerMap) -> {
HashMap<LocalDate, Duration> newStuff = new HashMap<>();
innerMap.forEach((k2,v2) -> newStuff.put(k2,Duration.ofSeconds(v2)));
newMap.put(key, newStuff);
});
And one more... 还有一个......
Map<String, Map<LocalDate, Duration>> newMap = map.entrySet().stream().collect(
Collectors.toMap(Entry::getKey,
entry -> entry.getValue().entrySet().stream().collect(
Collectors.toMap(Entry::getKey, e -> Duration.ofSeconds(e.getValue())))));
My two cents, create a method to transform Map<K, V1>
to a Map<K, V2>
: 我的两分钱,创建一个方法将Map<K, V1>
转换为Map<K, V2>
:
public static <K,V1,V2> Map<K, V2> transformValues(final Map<K, V1> input, final Function<V1,V2> transform) {
Function<Map.Entry<K, V1>, V2> mapper = transform.compose(Map.Entry::getValue);
return input.entrySet().stream()
.collect(toMap(Map.Entry::getKey, mapper));
}
Then your code becomes: 然后你的代码变成:
Map<String, Map<LocalDate, Duration>> transformed
= transformValues(maps, map -> transformValues(map, Duration::ofSeconds));
Let's extract a helper method toMap
to makes the problem more simpler. 让我们提取一个辅助方法toMap
来使问题更简单。
Map<String, Map<LocalDate, Duration>> result = oldMap.entrySet().stream()
.map(entry -> new AbstractMap.SimpleEntry<>(
entry.getKey(),
entry.getValue().entrySet().stream()
.collect(toMap(it -> Duration.ofSeconds(it.longValue())))
// ^--- mapping Integer to Duration
)).collect(toMap(Function.identity()));
<K, V, R> Collector<Map.Entry<K, V>, ?, Map<K, R>> toMap(Function<V, R> mapping) {
return Collectors.toMap(
Map.Entry::getKey,
mapping.compose(Map.Entry::getValue)
);
}
AND you can simplified the code as further since the primary logic is duplicated: adapts a value to another value in a Map
. 并且您可以进一步简化代码,因为主逻辑是重复的: 将值调整为Map
另一个值 。
Function<
Map<String, Map<LocalDate, Integer>>,
Map<String, Map<LocalDate, Duration>>
> mapping = mapping(mapping(seconds -> Duration.ofSeconds(seconds.longValue())));
// | |
// | adapts Map<LocalDate, Integer> to Map<LocalDate,Duration>
// |
//adapts Map<String,Map<LocalDate,Integer>> to Map<String,Map<LocalDate,Duration>>
Map<String, Map<LocalDate, Duration>> result = mapping.apply(oldMap);
<K, V1, V2> Function<Map<K, V1>, Map<K, V2>> mapping(Function<V1, V2> mapping) {
return it -> it.entrySet().stream().collect(toMap(mapping));
}
THEN you can use it anywhere that needs a Function<Map<?,?>,Map<?,?>>
to adapts the value to another value,for example: 那么你可以在任何需要Function<Map<?,?>,Map<?,?>>
地方使用它来将值调整为另一个值,例如:
Map<String, Map<LocalDate, Duration>> result = Optional.of(oldMap)
.map(mapping(mapping(seconds -> Duration.ofSeconds(seconds.longValue()))))
.get();
I would prefer to iterate over the entries of your old map and streaming over the inner map: 我更喜欢迭代旧地图的条目并在内部地图上流式传输:
for (Entry<String, Map<LocalDate, Integer>> entry : oldMap.entrySet()) {
Map<LocalDate, Duration> asDuration = entry.getValue().entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(), e -> Duration.ofSeconds(e.getValue().longValue())));
newMap.put(entry.getKey(), asDuration);
}
Otherwise you need a second stream inside your collect
: 否则,您需要在collect
使用第二个流:
newMap = oldMap.entrySet().stream()
.collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue().entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(), e -> Duration.ofSeconds(e.getValue().longValue())))));
If i understand you correct,you can see following code: 如果我理解你的错误,你可以看到以下代码:
oldMap.entrySet().stream()
.collect(Collectors.toMap(x -> x.getKey(),
x -> {
Map<LocalDate, Duration> temp = new HashMap<>();
x.getValue().forEach((k, v) -> {
temp.put(k, Duration.ofSeconds(v));
});
return temp;
})
);
For completeness, here's a version that uses Guava's Maps.transformValues
: 为了完整Maps.transformValues
,这是一个使用Guava的Maps.transformValues
的版本:
Map<String, Map<LocalDate, Duration>> result =
Maps.transformValues(oldMap, m -> Maps.transformValues(m, Duration::ofSeconds));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.