Collectors.groupingBy(功能,供应商,收集者)不接受lambda /看不到流值

时间:2019-02-04 16:18:18

标签: java java-8 java-stream

我尝试使用流和收集器对值进行分组。 我有要拆分的字符串列表。

我的数据:

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

2 个答案:

答案 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]}