Java 8 - 对列表进行分组并查找计数

Mad*_*han 5 java arraylist java-8 java-stream

我有一个结果列表。我需要找到通过的结果计数。但列表中的某些项目之间存在关系。例如。我有这样的清单

1.0 - false
2.0 - true
3.0 - false
4.0 - true
1.1 - true
3.1 - true
Run Code Online (Sandbox Code Playgroud)

那么通过的计数应该是 2 而不是 4。因为我想根据 id 对列表进行分组(1,1.2,1.3,1.xx 到单个组中),如果组中的所有项目都是,则将其标记为通过经过 。我尝试过小组使用groupingBy,并且得到了我预期行为的地图。我可以迭代地图并获取计数。但我想知道有什么方法可以简单地使用 Java 8 来做到这一点。

public class Main {

static class Resultx {

    double id = 1;

    Boolean passed = false;

    public void setId(double id) {
        this.id = id;
    }

    public double getId() {
        return id;
    }

    public void setAsPassed() {
        this.passed = true;
    }

    public Boolean getPassed() {
        return passed;
    }

    @Override
    public String toString() {
        return getId() + " - " + getPassed();
    }
}


public static void main(String[] args) {
    List<Resultx> results = new ArrayList<>();
    for (int i = 1; i < 5; i++) {
        Resultx result = new Resultx();
        result.setId(i);
        if (i % 2 == 0) {
            result.setAsPassed();
        }
        results.add(result);
    }
    for (int i = 1; i < 5; i += 2) {
        Resultx result = new Resultx();
        result.setId(i + .1);
        result.setAsPassed();
        results.add(result);
    }
    System.out.println(results.size());
    results.forEach(System.out::println);
    System.out.println(results.stream().filter(Resultx::getPassed).count());
    System.out.println(results.stream().filter(e -> !e.getPassed()).count());
    System.out.println(results.stream().collect(Collectors.groupingBy(r -> (int) (r.getId()))));
}
}
Run Code Online (Sandbox Code Playgroud)

输出

Total count - 6
1.0 - false
2.0 - true
3.0 - false
4.0 - true
1.1 - true
3.1 - true
Total pass count  - 4
Total fail count - 2
{1=[1.0 - false, 1.1 - true], 2=[2.0 - true], 3=[3.0 - false, 3.1 - true], 4=[4.0 - true]}
Run Code Online (Sandbox Code Playgroud)

我想要总体通过计数和总体失败计数。这是 2 和 2

SEY*_*_91 5

试试这个

 Map<Boolean, Long> collect = results.stream()
                    .collect(Collectors.groupingBy(r -> (int) (r.getId()))).values()
                    .stream().map(l -> l.stream().allMatch(p -> p.getPassed()))
                    .collect(Collectors.partitioningBy(k -> k, Collectors.counting()));

            System.out.println(collect);
Run Code Online (Sandbox Code Playgroud)

哪个显示:

{false=2, true=2}
Run Code Online (Sandbox Code Playgroud)


Hol*_*ger 3

您的代码有一些奇怪的事情,例如使用ID,您只是在分组操作中将double其转换为 ID ,或者使用for you属性而不是仅使用. 使用引用类型,打开了成为 的机会,如果打算这种可能性,您将必须处理它。否则,只需使用.intBooleanpassedbooleanBooleannullboolean

\n\n

它\xe2\x80\x99s也不清楚,你真正想要什么结果。这个例子不足以描述它。

\n\n

如果您只想计算组数,其中truemeans \xe2\x80\x9call where true\xe2\x80\x9d 和falsemeans \xe2\x80\x9csome where false\xe2\x80\x9d,你可以这样做简单

\n\n
Map<Boolean,Long> counts = results.stream()\n    .collect(Collectors.collectingAndThen(\n        Collectors.toMap(r -> (int)r.getId(), Resultx::getPassed, Boolean::logicalAnd),\n        m -> m.values().stream()\n            .collect(Collectors.partitioningBy(b -> b, Collectors.counting()))\n    ));\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果你想计算组中的元素,事情就会变得更加复杂。

\n\n
int totalPassedCount = results.stream()\n    .collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(),\n        Collector.of(() -> new Object() { int count = 0; boolean pass = true; },\n            (o, r) -> { o.count++; o.pass &= r.getPassed(); },\n            (x, y) -> { x.count += y.count; x.pass &= y.pass; return x; },\n            o -> o.pass? o.count: 0\n        )),\n        (Map<Integer,Integer> x) -> x.values().stream().mapToInt(i -> i).sum()\n    ));\nSystem.out.println(totalPassedCount);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这使用自定义收集器作为 的下游收集器groupingBy。此自定义收集器收集到一个对象中,该对象保存元素计数以及是否所有元素都通过了,然后,在完成步骤中,如果组中的所有元素都通过了,则用计数替换这些对象,否则为零。然后,向收集器添加一个整理步骤,groupingBy该步骤将汇总所有这些值。

\n\n
\n\n

上面的解决方案是为了获取通过的计数,正如您在问题开头所要求的那样。由于您\xe2\x80\x99最后要求两者,您可以使用

\n\n
Map<Boolean,Integer> counts = results.stream()\n    .collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(),\n        Collector.of(() -> new Object() { int count = 0; boolean pass = true; },\n            (o, r) -> { o.count++; o.pass &= r.getPassed(); },\n            (x, y) -> { x.count += y.count; x.pass &= y.pass; return x; }\n        )),\n        m -> m.values().stream().collect(\n            Collectors.partitioningBy(o -> o.pass, Collectors.summingInt(o -> o.count)))\n    ));\n
Run Code Online (Sandbox Code Playgroud)\n\n

为了两者兼得。

\n\n

或者,因为从单个示例中获取预期规则很困难,

\n\n
Map<Boolean,Integer> counts = results.stream()\n    .collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(),\n        Collector.of(() -> new Object() { int passed, failed; },\n            (o, r) -> { if(r.getPassed()) o.passed++; else o.failed++; },\n            (x, y) -> { x.passed += y.passed; x.failed += y.failed; return x; }\n        )),\n        m -> m.values().stream()\n            .filter(o -> o.passed == 0 || o.failed == 0)\n            .collect(Collectors.partitioningBy(o -> o.failed==0,\n                Collectors.summingInt(o -> o.failed==0? o.passed: o.failed)))\n    ));\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果组中的所有元素都通过或失败,则仅计算passed和。failed但既然你说过,你\xe2\x80\x99d期望22,你可以删除该.filter(o -> o.passed == 0 || o.failed == 0)行。passed然后,仅当组中的所有元素都通过时才会进行计数,但failed即使组中的某些元素通过了,也会对 all 进行计数。然后,你\xe2\x80\x99d得到2结果2

\n