有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

lambda Java 8收集器。具有映射值的groupingBy将收集结果设置为同一集合

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

import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

我需要使用结果值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<>());

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

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

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

我已经检查了一些收集者,但没有管理我的问题

问题是:

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


共 (3) 个答案

  1. # 1 楼答案

    这里有一个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());
    
  2. # 2 楼答案

    您可以使用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<>());
    
  3. # 3 楼答案

    如果使用更合适的数据结构,即multimap,这就不会那么复杂了

    多重映射存在,例如在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());
    

    我使其不可变(^{}),但也可以使用^{}获得可变版本