[英]how to use java streams api to transform a hierarchal map to a flat map
因此,我有一个使用映射的函数,其中一个名称与数组中多个不同索引之一相关联。 索引号将永远只有一个与之关联的名称,因此没有重复且没有null,因此可以安全地使用以下函数来平整层次结构。
public Map<Integer, Object> normalize( Map<Object, List<Integer>> hierarchalMap ) {
Map<Integer, Object> normalizedMap = new HashMap<>();
for (Map.Entry<Object, List<Integer>> entry : hierarchalMap.entrySet()) {
for (Integer integer : entry.getValue()) {
noramizedMap.put(integer, entry.getKey());
}
}
return normalizedMap;
}
我正在尝试将此功能更改为使用流API,到目前为止,我已经做到了:
Map<Integer, Object> noramizedMap = new HashMap<>();
for (Map.Entry<Object, List<Integer>> entry : vars.entrySet()) {
entry.getValue().forEach(e -> noramizedMap.put(e, entry.getValue()));
}
如果这是其他某种功能性语言,我会做部分绑定或其他操作,但是使用java时,我尝试将外循环展开为流... collectTo我只是迷路了。
假设我的评论正确,则可以使用以下Streams:
hierarchalMap.entrySet()
.stream()
.flatMap(entry -> entry.getValue()
.stream()
.map(i -> new AbstractMap.SimpleEntry<>(entry.getKey(), i)))
.collect(Collectors.toMap(Entry::getValue, Entry::getKey));
假定没有重复且没有null。
我认为这应该可以满足您的需求:
public Map<Integer, Object> normalizeJava8(Map<Object, List<Integer>> hierarchalMap ) {
return hierarchalMap
.entrySet()
.stream()
.collect(
HashMap::new,
(map, entry) ->
entry.getValue().forEach(i -> map.put(i, entry.getKey())),
HashMap::putAll);
}
通常,在使用Java 8流时,由于缺少便捷的元组类型,与其他语言的等效构造相比,您必须在操作的“收集”部分中放置更多的逻辑。 直观地讲,创建成对的列表然后将它们收集到一个映射中似乎更为理智,但是与将逻辑放入.collect
相比,最终导致更多的代码和更高的计算强度。
使用流和Java 9:
Map<Integer, Object> normalizedMap = hierarchalMap.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(i -> Map.entry(i, e.getKey())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
这与该答案几乎相同,除了我使用Map.entry()
方法创建对,并将整数作为键。
这是不使用流的另一种不太冗长的方法:
Map<Integer, Object> normalizedMap = new HashMap<>();
hierarchalMap.forEach((k, v) -> v.forEach(i -> normalizedMap.put(i, k)));
您可以在Java 8中使用以下两个便利收集器,它们不仅限于地图。
public static <T, K, V> Collector<T, ?, Map<K, V>> flatInverseMapping(Function<? super T, ? extends Stream<? extends K>> keyStreamFunction,
Function<? super T, ? extends V> valueFunction) {
return Collector.of(HashMap::new,
(m, v) ->
keyStreamFunction.apply(v).forEach(innerV -> m.put(innerV, valueFunction.apply(v))),
(m1, m2) -> {
m1.putAll(m2);
return m2;
});
}
public static <T, K, V> Collector<T, ?, Map<K, V>> flatInverseMapping(Function<? super T, ? extends Collection<? extends K>> keyStreamFunction,
Function<? super T, ? extends V> valueFunction) {
return Collector.of(HashMap::new,
(m, v) ->
keyStreamFunction.apply(v).forEach(innerV -> m.put(innerV, valueFunction.apply(v))),
(m1, m2) -> {
m1.putAll(m2);
return m2;
});
}
由于流和集合都具有forEach,因此除了输入对象外,这两种实现都相同。
为了简要说明这是如何工作的,收集器的输出是由两个函数的输出定义的K和V(键和值)参数的映射。 对于流中从输入对象派生的每个键值,将应用相同的值函数,以便在共享键之间使用一致的值反转映射。 请注意,如果流中有多个项目可以解析为相同的键,则不会像正常的toMap实现那样抛出合并异常。 BiConsumer将需要更改为此,以保持该行为:
(var1, var2) -> {
Iterator<Map.Entry<K,V>> var3 = var2.entrySet().iterator();
while(var3.hasNext()) {
Map.Entry<K,V> var4 = var3.next();
var1.merge(var4.getKey(), var4.getValue(), (v0, v1) -> {
throw new IllegalStateException(String.format("Duplicate key %s", v0));
});
}
return var1;
}
作为参考,它实际上是从Collectors.toMap代码复制的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.