Java 8 Collectors.groupingBy 具有映射值以将收集结果设置为同一集合
Java 8 Collectors.groupingBy with mapped value to set collecting result to the same set
示例中使用的对象来自包 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<>());
它似乎收集正确,但值始终是单个字符串(由于库实现)包含由 space 分割的不同值。正在尝试改进解决方案:
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>>
我检查了一些收集器,但没有解决我的问题。
问题是:
如何合并与同一属性键相关的所有集合?
您可以使用 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<>());
这是一个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());
如果您使用更合适的数据结构,即 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());
我让它不可变 (ImmutableSetMultimap
), but a mutable version can also be obtained using Multimaps.flatteningToMultimap
.
示例中使用的对象来自包 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<>());
它似乎收集正确,但值始终是单个字符串(由于库实现)包含由 space 分割的不同值。正在尝试改进解决方案:
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>>
我检查了一些收集器,但没有解决我的问题。
问题是:
如何合并与同一属性键相关的所有集合?
您可以使用 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<>());
这是一个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());
如果您使用更合适的数据结构,即 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());
我让它不可变 (ImmutableSetMultimap
), but a mutable version can also be obtained using Multimaps.flatteningToMultimap
.