【问题标题】:java 8 grouping by with distinct countjava 8按不同计数分组
【发布时间】:2019-10-12 18:41:52
【问题描述】:
SELECT Count(1) AS total,
          'hello' AS filter,
          field1 AS field1,
          Count(DISTINCT field2) AS total_field2
   FROM table
   WHERE field = true
     AND status = 'ok'
      GROUP  BY field1

怀疑如何使用java8制作地图来存储以下结果。映射键必须是字段field1,映射值必须是total_field2 字段。

也就是说,我需要使用字段 field1 和计数字段 field2 对列表进行分组

对于我拥有的总领域

myList.stream().collect(Collectors.groupingBy(MyObject::getField1, Collectors.counting())) 
// this is just counting the records grouped by field1

我的结果是正确的total_field1: {4=55, 6=31}

对于field2,我需要这样的东西,但这只是给我一个记录

myList.stream().filter(distinctByKey(MyObject::getField2))
.collect(Collectors.groupingBy(MyObject::getField1, Collectors.counting()));

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }

结果 total_Field2:{4=31}

应该返回 2 条示例记录 total_Field2: {4=31, 6=31}

示例 @Naman

public static <T, A, R> Collector<T, ?, R> filtering(
        Predicate<? super T> predicate, Collector<? super T, A, R> downstream) {

        BiConsumer<A, ? super T> accumulator = downstream.accumulator();
        return Collector.of(downstream.supplier(),
            (r, t) -> { if(predicate.test(t)) accumulator.accept(r, t); },
            downstream.combiner(), downstream.finisher(),
            downstream.characteristics().toArray(new Collector.Characteristics[0]));
    }

myList.stream().collect(Collectors.groupingBy(MyObject::getField1, filtering(distinctByKey(MyObject::getField2), Collectors.counting())));

【问题讨论】:

  • 你忘了问问题。
  • 请记住How to Ask
  • 如果我知道你想按field1 分组,field2 的不同计数是对的吗?
  • @Deadpool,这就是我想要完成的,但我没有得到
  • @JoSSte 抱歉,我会改进一个问题

标签: java java-8 java-stream grouping collectors


【解决方案1】:

其实我用Set 来消除重复,Collectors.collectingAndThen 来获取大小

Map<String, Integer> res =  list.stream()
                                .collect(Collectors.groupingBy(MyObject::getField1, 
                                        Collectors.mapping(MyObject::getField2, 
                                            Collectors.collectingAndThen(Collectors.toSet(), set->set.size()))));

根据@Naman 的建议,您也可以使用方法参考Set::size

Collectors.collectingAndThen(Collectors.toSet(), Set::size))));

【讨论】:

  • ..further Set::size for set-&gt;set.size()
【解决方案2】:

Deadpool's answer 的替代方法是在 groupingBy field1 映射到条目然后最终收集到 Map 之后计数 distinctByKey

Map<String, Long> r = myList.stream()
        .collect(Collectors.groupingBy(MyObject::getField1))
        .entrySet().stream()
        .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(),
                e.getValue().stream().filter(distinctByKey(MyObject::getField2)).count()))
        .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));

罢工>

如果您使用的是 Java-9 或更高版本,则可以将 Collectors.filtering 用作 downstream,并使用实用程序 distinctByKey 定义 Predicate,例如:

Map<String, Long> result = myList.stream()
        .collect(Collectors.groupingBy(MyObject::getField1,
                Collectors.filtering(distinctByKey(MyObject::getField2),
                        Collectors.counting())));


注意:虽然上述两种方法有很大不同,前者将所有列表项按一个字段(field1)分组,然后在每个子组中通过另一个特定字段找到不同的计数( field2)。

另一方面,后者通过键 (field2) 将所有不同的项目分组,然后通过另一个键 (field1) 将它们分组并减少计数。

【讨论】:

  • 工作带来了两条记录,但一条重置 {4=31, 6=0} 六条应该带有 31
  • @EdeGerSil 是的,我的错。 Collectors.filtering,将源中的对象过滤为实际集合,与此处使用filter(distinctByKey(MyObject::getField2)) 几乎相同。
  • 对不起@Naman 我不明白解决方案。我根据留下对 java8 的引用的示例制作我在我的问题中插入了我所做的编码,如果它可以提供帮助
【解决方案3】:

你可以试试这个:

myList.stream().map(obj -> Pair.of(obj.getField1(), obj.getField2()))
      .distinct()
      .collect(Collectors.groupingBy(Pair::getLeft, counting()));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 2012-07-29
    • 2023-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    相关资源
    最近更新 更多