我尝试使用流和收集器对值进行分组。 我有要拆分的字符串列表。
我的数据:
List<String> stringList = new ArrayList<>();
stringList.add("Key:1,2,3")
stringList.add("Key:5,6,7")
键是地图中的键, 1,2,3 是地图中的值
首先,我尝试使用简单的toMap
Map<String, List<Integer>> outputKeyMap = stringList.stream()
.collect(Collectors.toMap(id -> id.split(":")[0],
id-> Arrays.stream(id.split(":")[1].split(",")).collect(Collectors.toList());
,但是它总是起作用,因为它总是创建相同的密钥。所以我需要使用groupingBy
函数。
Map<String, List<Integer>> outputKeyMap = stringList.stream().collect(groupingBy(id -> id.toString().split(":")[0],
TreeMap::new,
Collectors.mapping(id-> Arrays.stream(id.toString().split(":")[1].split(","))
.map(Integer::valueOf)
.collect(Collectors.toSet()))));
但是在此解决方案中,编译器没有看到将值传递给lambda函数,并且我不为什么,因为Function
是第一个参数,也是Collectors.mapping
。在此解决方案中,流不起作用。
Collectors.groupingBy (Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)
编辑:为什么分组功能不起作用
我忘记在Collectors.mapping中添加Collectors.toSet()作为第二个参数。但是后来我收到Set in Set,所以它不是我想要的。应该使用flatMapping,但是它在Java9中。
Map<String, Set<Set<String>>> collect = stringList.stream()
.collect(groupingBy(id -> id.split(":")[0],
TreeMap::new,
Collectors.mapping(id-> Arrays.stream(id.toString().split(":")[1].split(","),
Collectors.toSet())
答案 0 :(得分:4)
您必须使用Collectors.toMap
的重载,该重载接受合并功能:
Map<String, List<Integer>> result = stringList.stream()
.map(string -> string.split(":"))
.collect(Collectors.toMap(
splitted -> splitted[0],
splitted -> Arrays.stream(splitted[1].split(","))
.map(Integer::valueOf)
.collect(Collectors.toCollection(ArrayList::new)),
(l1, l2) -> { l1.addAll(l2); return l1; }));
这里(l1, l2) -> { l1.addAll(l2); return l1; }
是合并功能。每当有按键碰撞时,收集器都会调用它。在List.addAll
更改列表时,我们需要确保创建的第一个列表是可变的,因此在值映射器函数中使用.collect(Collectors.toCollection(ArrayList::new))
。
我还将第一次拆分优化为在收集之前调用的Stream.map
操作,从而避免了多次拆分。
上述解决方案不会从列表中删除重复项。如果需要,请改为收集到Set
:
Map<String, Set<Integer>> result = stringList.stream()
.map(string -> string.split(":"))
.collect(Collectors.toMap(
splitted -> splitted[0],
splitted -> Arrays.stream(splitted[1].split(","))
.map(Integer::valueOf)
.collect(Collectors.toCollection(LinkedHashSet::new)),
(s1, s2) -> { s1.addAll(s2); return s1; }));
请注意,LinkedHashSet
保留插入顺序。
答案 1 :(得分:3)
假设您的源列表中没有重复的密钥,则可以像这样获得Map<String, List<Integer>>
:
Map<String, List<Integer>> result = stringList.stream()
.collect(toMap(string -> string.split(":")[0],
string -> Arrays.stream(string.split(":")[1].split(","))
.map(Integer::valueOf)
.collect(toList())));
如果您有重复的键,则可以使用java9中的flatMapping
方法:
Map<String, List<Integer>> result = stringList.stream()
.collect(groupingBy(s -> s.split(":")[0],
flatMapping(s -> Arrays.stream(s.split(":")[1].split(","))
.map(Integer::valueOf),
toList())));
输出将包含Key
的所有整数值:
{Key=[1, 2, 3, 5, 6, 7]}