繁体   English   中英

Java8:操作 Map 的有效方法

[英]Java8 : Efficient approach to manipulate Map

我有两个地图定义的地图,如下共享,基于 map1 中一个键的存在,我需要将条目复制到另一个地图。 下面的代码根据需要工作,但有没有更好和简单的方法来实现相同的目标。 您的建议表示赞赏。

    Map<Object, HashSet<Map<Object, String>>> map1 = new HashMap<>();
    Map<Object, HashSet<Map<Object, String>>> map2 = new HashMap<>();

    map1.entrySet().stream().forEach(e -> {
        Set<Map<Object, String>> infoMapSet = e.getValue();
        infoMapSet.forEach(
                inMap -> {
                    if (inMap.containsKey("isMerge")) {
                        Set<Map<Object, String>> set = new HashSet<>();
                        set.add(inMap);
                        map2.put(e.getKey(), set);
                    }
                });
    });

我写了一个功能更强大的方法,在我看来,它使代码更具可读性。 但是正如评论者已经指出的那样,您永远不会真正获得具有这种数据结构的简单、可读的解决方案。

首先,我将您的函数重写为实际函数,即取值并返回值。 因为你在上面所做的,本质上是取一个数据结构并过滤它。 在下面的代码中,我添加了您的原始函数并添加了一个更实用的函数。 请注意,我添加了 Vavr 作为依赖项,有关更多详细信息,请参阅Vavr (但仅将其用于 Tuple2 类,因此可以轻松地替换为您自己的实现)。

package com.example.demo;

import io.vavr.Tuple2;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class LegacyDataStructure {
    public static Map<Object, Set<Map<Object,String>>> filteringFunction1(Map<Object, Set<Map<Object,String>>> origin) {
        Map<Object,Set<Map<Object,String>>> map2 = new HashMap<>();

        origin.entrySet().stream().forEach(e -> {
            Set<Map<Object,String>> setInfo= e.getValue();
            setInfo.forEach(inMap -> {
                if(inMap.containsKey("isMerge")) {
                    HashSet<Map<Object,String>> set = new HashSet<>();
                    set.add(inMap);
                    map2.put(e.getKey(), set);
                }
            });
        });

        return map2;
    }

    public static Map<Object, Set<Map<Object, String>>> filteringFunction2(Map<Object, Set<Map<Object, String>>> origin) {
        return origin
                .entrySet()
                .stream()
                .map(e -> new Tuple2<>(
                                e.getKey(),
                                e.getValue()
                                        .stream()
                                        .filter(m -> m.containsKey("isMerge"))
                                        .collect(Collectors.toSet())
                        )
                )
                .filter(t -> t._2().size() > 0)
                .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2));
    }
}

您在filteringFunction2 中看到的情况是,我们获取entrySet 并将键与符合条件的值相关联,并过滤掉任何具有零匹配条件的键。

这是一个测试来证明这两个函数在它们产生的结果上是相同的:

package com.example.demo;

import io.vavr.Tuple2;
import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class FilteringTests {
    @Test
    public void bothFilteringMethodsShouldYieldSameResults() {
        Map<Object, Set<Map<Object,String>>> testMap = new HashMap<>();

        testMap.put(1, Set.of(Map.of("isMerge", "foo")));
        testMap.put(2, Set.of(Map.of("notMerge", "bar")));
        testMap.put(3, Set.of(Map.of("notMerge", "bar")));

        var results1 = LegacyDataStructure.filteringFunction1(testMap);
        var results2 = LegacyDataStructure.filteringFunction2(testMap);

        assertEquals(1, results1.keySet().size());
        assertEquals(1, results2.keySet().size());

        results1.entrySet().forEach(e -> {
            assertEquals(1, e.getKey());
            var tuple = extractFirstEntry(e.getValue());
            assertEquals("isMerge", tuple._1());
            assertEquals("foo", tuple._2());
        });

        results2.entrySet().forEach(e -> {
            assertEquals(1, e.getKey());
            var tuple = extractFirstEntry(e.getValue());
            assertEquals("isMerge", tuple._1());
            assertEquals("foo", tuple._2());
        });
    }

    private Tuple2<Object, String> extractFirstEntry(Set<Map<Object,String>> valueSet) {
        return valueSet
                .stream()
                .findFirst()
                .get()
                .entrySet()
                .stream()
                .map(m -> new Tuple2<>(m.getKey(), m.getValue()))
                .findFirst()
                .get();
    }
}

您可以通过使用Map.forEachMap.computeIfAbsent来提高代码的可读性和表达性:

Map<Object, Set<Map<Object, String>>> map2 = new LinkedHashMap<>();

map1.forEach((key, setInfo) ->
    setInfo.forEach(inMap -> {
        if (inMap.containsKey("isMerge")) 
            map2.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(inMap);
    }));

注意:我使用LinekdHashMapLinkedHashSet来保留插入顺序。

同一个setInfo HashSet 是否有可能具有多个满足此条件的映射?

如果是,则您的代码仅复制最后一张地图,因为前一张地图正在被替换。 所以代码应该固定如下。

map1.entrySet().stream().forEach(e -> {
    HashSet<Map<Object,String>> setInfo= e.getValue();

    HashSet<Map<Object, String>> set = new HashSet<>();
    setInfo.forEach(inMap -> {
        if (inMap.containsKey("isMerge")) {
            set.add(inMap);
        }
    });
    if (!set.isEmpty()) {
        map2.put(e.getKey(), set);
    }
});

否则,如果你保证同一个setInfo HashSet 只能有一个满足这个条件的映射,那么一旦找到这样的映射,你就可以break setInfo的迭代。

map1.entrySet().stream().forEach(e -> {
    HashSet<Map<Object,String>> setInfo= e.getValue();
    for (Map<Object, String> inMap: setInfo) {
        if (inMap.containsKey("isMerge")) {
            HashSet<Map<Object, String>> set = new HashSet<>();
            set.add(inMap);
            map2.put(e.getKey(), set);
            break;
        }
    }
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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