java 8 按不同计数进行分组

Ede*_*Sil 7 java grouping java-8 java-stream collectors

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
Run Code Online (Sandbox Code Playgroud)

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

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

对于我的总字段

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

我的结果是正确的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));
    }
Run Code Online (Sandbox Code Playgroud)

结果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())));
Run Code Online (Sandbox Code Playgroud)

Dea*_*ool 7

实际上我曾经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()))));
Run Code Online (Sandbox Code Playgroud)

根据@Naman的建议,您还可以使用方法引用Set::size

Collectors.collectingAndThen(Collectors.toSet(), Set::size))));
Run Code Online (Sandbox Code Playgroud)

  • ..进一步为`set-&gt;set.size()`设置`Set::size` (2认同)
  • 这是很好的答案 (2认同)

Nam*_*man 2

Deadpool 的答案的替代方法是在映射到条目时distinctByKey在 groupingBy 之后进行计数field1,然后最终收集到Mapas:

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));
Run Code Online (Sandbox Code Playgroud)

如果您使用的是 Java-9 或更高版本,您可以使用以下实用程序Collectors.filteringdownstream定义:PredicatedistinctByKey

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


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

另一方面,后者通过 key( field2) 对所有不同的项进行分组,然后通过另一个 key( field1) 对这些项进行分组,并减少计数。