Java 8 Collectors.groupingBy with map value将收集结果设置为同一个集合

Ser*_*gii 14 java lambda java-8 java-stream collectors

示例中使用的对象来自包 org.jsoup.nodes

import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
Run Code Online (Sandbox Code Playgroud)

我需要按键的组属性和结果值Set.

Optional<Element> buttonOpt = ...;
Map<String, Set<String>> stringStringMap =
    buttonOpt.map(button -> button.attributes().asList().stream()
            .collect(groupingBy(Attribute::getKey, 
                  mapping(attribute -> attribute.getValue(), toSet()))))
            .orElse(new HashMap<>());
Run Code Online (Sandbox Code Playgroud)

它似乎正确收集,但值始终是单个字符串(因为库实现)包含按空格分割的不同值.试图改善解决方案:

Map<String, Set<HashSet<String>>> stringSetMap = buttonOpt.map(
        button -> button.attributes()
            .asList()
            .stream()
            .collect(groupingBy(Attribute::getKey, 
                        mapping(attribute -> 
                          new HashSet<String>(Arrays.asList(attribute.getValue()
                                                                .split(" "))),
                   toSet()))))
  .orElse(new HashMap<>());
Run Code Online (Sandbox Code Playgroud)

结果我有不同的结构,Map<String, Set<HashSet<String>>>但我需要Map<String, Set<String>>

我已经检查了一些收藏家,但没有解决我的问题.

问题是:

如何合并与相同属性键相关的所有集合?

Rav*_*ala 13

这是Java9的做法,

Map<String, Set<String>> stringSetMap = buttonOpt
    .map(button -> button.attributes().asList().stream()
        .collect(Collectors.groupingBy(Attribute::getKey, Collectors.flatMapping(
            attribute -> Arrays.stream(attribute.getValue().split(" ")), Collectors.toSet()))))
    .orElse(Collections.emptyMap());
Run Code Online (Sandbox Code Playgroud)

  • 太好了!我只建议`orElseGet(HashMap :: new)`而不是`orElse(new HashMap <>())`来使它更清洁一点. (3认同)
  • `orElse(HashMap :: new)`也不会编译...对它进行编辑. (2认同)

Era*_*ran 12

您可以拆分属性flatMap并创建新条目以进行分组:

Optional<Element> buttonOpt = ...
Map<String, Set<String>> stringStringMap =
        buttonOpt.map(button -> 
            button.attributes()
                  .asList()
                  .stream()
                  .flatMap(at -> Arrays.stream(at.getValue().split(" "))
                                       .map(v -> new SimpleEntry<>(at.getKey(),v)))
                  .collect(groupingBy(Map.Entry::getKey, 
                                      mapping(Map.Entry::getValue, toSet()))))
                .orElse(new HashMap<>());
Run Code Online (Sandbox Code Playgroud)

  • 我建议使用`orElse(Collections.emptyMap())`,因为不需要实例化一个新的`HashMap`(并且没有地图供应商的`groupingBy`收集器不能保证生成`HashMap`,所以调用者不应该假设它). (2认同)

Tom*_*ski 7

如果您使用更合适的数据结构为它,即这个问题就不那么复杂的多重映射.

Multimaps存在于例如Guava中,您可以按如下方式执行此操作:

SetMultimap<String, String> stringMultimap = buttonOpt
        .map(button -> button.attributes().asList().stream()
                .collect(ImmutableSetMultimap.flatteningToImmutableSetMultimap(
                        Attribute::getKey,
                        attribute -> Arrays.stream(attribute.getValue().split(" "))
                ))
        ).orElse(ImmutableSetMultimap.of());
Run Code Online (Sandbox Code Playgroud)

我把它变成了immutable(ImmutableSetMultimap),但是也可以使用一个可变版本Multimaps.flatteningToMultimap.