【问题标题】:Stream grouping where elements can belong to more than one group元素可以属于多个组的流分组
【发布时间】:2020-10-30 12:26:57
【问题描述】:

我正在尝试使用流 api groupingby 收集器来获取映射 groupId -> List of elements。我的案例的特殊之处在于一个元素可以属于多个组。

用一个简单的例子来演示:假设我想使用数字2 - 10作为分组的标识符,并希望对数字2 - 40进行分组,这样它们就可以看作是我的标识符的倍数。传统上我会这样做:

Map<Integer,List<Integer>> map = new HashMap<>();
    for(int i = 2; i < 11; i++){
        for(int j = 2; j < 41; j++){
            if(j%i == 0)
            map.computeIfAbsent(i, k -> new ArrayList<>()).add(j);
        }
    }
    map.forEach((k,v) -> {
        System.out.println(k + " : " + v);
    });

得到类似的东西

2 : [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40]
3 : [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39]
4 : [4, 8, 12, 16, 20, 24, 28, 32, 36, 40]
5 : [5, 10, 15, 20, 25, 30, 35, 40]
6 : [6, 12, 18, 24, 30, 36]
7 : [7, 14, 21, 28, 35]
8 : [8, 16, 24, 32, 40]
9 : [9, 18, 27, 36]
10 : [10, 20, 30, 40]

为了使用流,我尝试将this 问题的答案应用于我的案例,但没有成功。

IntStream.range(2, 11).boxed()
            .flatMap(g -> IntStream.range(2, 41)
                .boxed()
                .filter(i -> i%g == 0)
                .map(i -> new AbstractMap.SimpleEntry<>(g,i))
            .collect(Collectors.groupingBy(Map.Entry::getKey, 
                    Collectors.mapping(Map.Entry::getValue, Collectors.toList()))));

我得到一个编译错误

不兼容的类型:推理变量 R#1 的边界不兼容 等式约束: Map 下界:流,对象 其中 R#1,A#1,T#1,K,T#2,A#2,D,R#2 是类型变量: R#1 扩展了在方法 collect(Collector super T#1,A#1,R#1>) 中声明的对象 A#1 扩展了在方法 collect(Collector super T#1,A#1,R#1>) 中声明的对象 T#1 扩展在接口 Stream 中声明的对象 K 扩展方法中声明的对象 groupingBy(Function super T#2,? extends K>,Collector super T#2,A#2,D>) T#2 扩展方法中声明的对象 groupingBy(Function super T#2,? extends K>,Collector super T#2,A#2,D> ) A#2 扩展方法中声明的对象 groupingBy(Function super T#2,? extends K>,Collector super T#2,A#2,D> ) D 扩展方法中声明的对象 groupingBy(Function super T#2,? extends K>,Collector super T#2,A#2,D>) R#2 扩展方法中声明的对象 flatMap(Function super T#1,? extends Stream extends R#2>>)


我做错了什么?

请注意,我的原始案例不是将数字分配给它们的倍数。实际上,我的组 id 具有长值,并且列表包含自定义对象。但是当我解决了上面的例子时,我想我可以将它应用到我的案例中。我只是想用简单的方式描述问题

【问题讨论】:

  • 您使用map.computeIfAbsent 的原始代码远比流版本好得多。它在性能和可读性/表现力方面也更好
  • @fps 你是对的。但我正在为初级开发人员学习一门课程,我必须想出一个使用流的解决方案。

标签: java java-stream groupingby


【解决方案1】:

你的意思是这样的?

Map<Integer,List<Integer>> v = IntStream.range(2, 11).boxed()
               .map(g -> IntStream.range(2, 41)
                       .boxed()
                       .filter(i -> i % g == 0)
                       .map(i -> new AbstractMap.SimpleEntry<>(g, i))
                       .collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getKey,
                               Collectors.mapping(AbstractMap.SimpleEntry::getValue, Collectors.toList()))))
               .flatMap(m -> m.entrySet().stream())
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

【讨论】:

  • 感谢您的回复,但我需要生成的地图类型为 Map&lt;Integer,List&lt;Integer&gt;&gt;
  • 您的建议确实很有帮助。我用.flatMap(m -&gt; m.entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 替换了最后一个.collect(Collectors.toList()); 并得到了想要的结果。谢谢
  • 只是一个快速破解,但我认为,它可以以更简单的方式完成。啊,毕竟还不错。
猜你喜欢
  • 2020-11-08
  • 2019-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
  • 1970-01-01
相关资源
最近更新 更多