首页 > 解决方案 > Java8通过值中包含的键创建映射分组

问题描述

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

{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 中的最佳解决方案吗?它可以以某种方式进行调整吗?

标签: collectionsjava-8hashmap

解决方案


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

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()));

推荐阅读