【问题标题】:Java8 Create Map grouping by key contained in valuesJava8通过值中包含的键创建映射分组
【发布时间】:2018-12-11 23:56:04
【问题描述】:

我有以下两个字符串列表:

{APPLE, ORANGE, BANANA} //call it keyList
{APPLE123, ORANGEXXX, 1APPLE, APPLEEEE} //call it valueList

所需的输出是HashMap<String, List<String>>,如下所示:

<APPLE, {APPLE123, 1APPLE, APPLEEEE}>
<ORANGE, {ORANGEXXX}>
<BANANA, {}> //also <key, null> is accepted

我已经实现了这个解决方案(它有效)

HashMap<String, List<String>> myMap = new HashMap<>();
keyList.forEach(key -> {
    List<String> values = valueList.stream()
            .filter(value -> value.contains(key))
            .collect(Collectors.toList());
    myMap.put(key, values);
});

假设一个值仅与一个键相关(它是我的域的约束),就性能和/或代码清理而言,这是 java8 中的最佳解决方案吗? 可以通过某种方式进行调整吗?

【问题讨论】:

    标签: collections java-8 hashmap


    【解决方案1】:

    如果您可以安全地假设每个值都与一个键相关联,并且只有一个键,那么您可以进入以下方向:

    Pattern p = Pattern.compile(String.join("|", keyList));
    Map<String, List<String>> map = valueList.stream()
        .collect(Collectors.groupingBy(s -> {
            Matcher m = p.matcher(s);
            if(!m.find()) throw new AssertionError();
            return m.group();
        }));
    
    map.forEach((k,v) -> System.out.println(k+": "+v));
    

    如果键可能包含可能被误解为正则表达式结构的特殊字符,您可以将准备代码更改为

    Pattern p = Pattern.compile(
        keyList.stream().map(Pattern::quote).collect(Collectors.joining("|")));
    

    collect 操作只会为现有值创建组。如果您确实需要所有键都存在,则可以使用

    Map<String, List<String>> map = valueList.stream()
        .collect(Collectors.groupingBy(s -> {
                Matcher m = p.matcher(s);
                if(!m.find()) throw new AssertionError();
                return m.group();
            },
            HashMap::new, // ensure mutable map
            Collectors.toList()
        ));
    keyList.forEach(key -> map.putIfAbsent(key, Collections.emptyList()));
    

    Pattern p = Pattern.compile(
        keyList.stream().map(Pattern::quote)
               .collect(Collectors.joining("|", ".*(", ").*")));
    Map<String, List<String>> map = valueList.stream()
        .map(p::matcher)
        .filter(Matcher::matches)
        .collect(Collectors.groupingBy(m -> m.group(1),
            HashMap::new, // ensure mutable map
            Collectors.mapping(Matcher::group, Collectors.toList())
        ));
    keyList.forEach(key -> map.putIfAbsent(key, Collections.emptyList()));
    

    【讨论】:

    • 非常感谢您提供的详细示例。我将尝试最后两个选项。据我了解,您的解决方案避免为每个键循环 valueList(也许它在木头下进行,但以一种有效的方式)。
    • 请记住,String.contains 也有一个内部循环。所以你的原始代码中基本上有for each value(for each key(for each character(…))),而只有最里面的循环可以在匹配时停止。所以你至少有number of keys × number of values 操作。我的解决方案基本上是for each value(for each character(for each key(…))),它可以在第一次匹配时停止“for each character”,当我们知道总会有匹配时,效率更高。此外,正如您所怀疑的,正则表达式引擎完成的最内层循环也更有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多