繁体   English   中英

如何使用Java Streams API将层次结构映射图转换为平面映射图

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM