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

时间:2018-10-30 15:31:33

标签: java arraylist java-8 java-stream

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

1.0 - false
2.0 - true
3.0 - false
4.0 - true
1.1 - true
3.1 - true

然后,传递的计数应该是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()))));
}
}

输出

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

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

5 个答案:

答案 0 :(得分:3)

尝试这个

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

哪个显示:

{false=2, true=2}

答案 1 :(得分:2)

关于代码,有些奇怪的事情,例如使用double ID(您刚刚在分组操作中将其强制转换为int)或为您{{1} }属性,而不仅仅是Boolean。使用引用类型passed,打开机会是boolean,如果有这种可能的话,您将不得不处理。否则,只需使用Boolean

也不清楚您实际上想要什么结果。该示例不足以对其进行描述。

如果您只想计算组数,其中null表示“所有正确的地方”,而boolean表示“某些在true的地方”,则可以像这样简单 >

false

如果要计算组的元素,它将变得更加复杂。

false

这将自定义收集器用作Map<Boolean,Long> counts = results.stream() .collect(Collectors.collectingAndThen( Collectors.toMap(r -> (int)r.getId(), Resultx::getPassed, Boolean::logicalAnd), m -> m.values().stream() .collect(Collectors.partitioningBy(b -> b, Collectors.counting())) )); 下游收集器。此自定义收集器将收集元素数量和是否所有元素通过的对象收集到一个对象中,然后在完成步骤中,如果组中的所有元素都通过,则用计数替换这些对象;否则,将其替换为零。然后,将完成步骤添加到int totalPassedCount = results.stream() .collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(), Collector.of(() -> new Object() { int count = 0; boolean pass = true; }, (o, r) -> { o.count++; o.pass &= r.getPassed(); }, (x, y) -> { x.count += y.count; x.pass &= y.pass; return x; }, o -> o.pass? o.count: 0 )), (Map<Integer,Integer> x) -> x.values().stream().mapToInt(i -> i).sum() )); System.out.println(totalPassedCount); 收集器中,该步骤将汇总所有这些值。


上面的解决方案是按照问题开头的要求获取通过的计数。由于最后都要求两者,因此可以使用

groupingBy

同时获得两者。

或者,很难从一个示例中获得预期的规则,

groupingBy

如果组中的所有元素均通过或失败,则仅计算Map<Boolean,Integer> counts = results.stream() .collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(), Collector.of(() -> new Object() { int count = 0; boolean pass = true; }, (o, r) -> { o.count++; o.pass &= r.getPassed(); }, (x, y) -> { x.count += y.count; x.pass &= y.pass; return x; } )), m -> m.values().stream().collect( Collectors.partitioningBy(o -> o.pass, Collectors.summingInt(o -> o.count))) )); Map<Boolean,Integer> counts = results.stream() .collect(Collectors.collectingAndThen(Collectors.groupingBy(r -> (int)r.getId(), Collector.of(() -> new Object() { int passed, failed; }, (o, r) -> { if(r.getPassed()) o.passed++; else o.failed++; }, (x, y) -> { x.passed += y.passed; x.failed += y.failed; return x; } )), m -> m.values().stream() .filter(o -> o.passed == 0 || o.failed == 0) .collect(Collectors.partitioningBy(o -> o.failed==0, Collectors.summingInt(o -> o.failed==0? o.passed: o.failed))) )); 。但是,正如您所说的那样,您可能期望passedfailed,因此可以删除2行。然后,仅当组中的所有元素都通过时,它才会计数2,但即使组中的某些元素都通过时,也将计数所有.filter(o -> o.passed == 0 || o.failed == 0)。然后,您将得到passedfailed的结果。

答案 2 :(得分:0)

这很简单,只需根据测试编号对结果进行排序,然后分别对每个组进行计数。只要您处理的测试少于一百万次,排序就不会那么复杂。

如果“组”中的单个“测试”失败,则该组作为一个整体失败,然后只有一个结构来存储每个分组的结果。

答案 3 :(得分:0)

我不确定这是最简单的方法,但是:

Map<Double, Boolean> map = new HashMap<>();
    map.put(1.0D, false);
    map.put(2.0D, true);
    map.put(2.1D, true);
    map.put(1.2D, true);

    Map<Boolean, Long> result = map.entrySet()
        .stream()
        .collect(Collectors.groupingBy(
                x -> Arrays.asList(Math.round(x.getKey()), x.getValue()),
                Collectors.counting()))
        .entrySet()
        .stream()
        .collect(Collectors.partitioningBy(
                e -> (Boolean) e.getKey().get(1),
                Collectors.counting()));


  System.out.println(result); // {false=1, true=2}

答案 4 :(得分:0)

不确定 Java 8 集合API是否将是最简单的方法,但是这里提供了一个实现,它可以计算肯定和否定结果的数量:

System.out.println(
              results.stream()
                    .collect(Collectors.groupingBy(result -> (int) (result.getId())))
                    .values()
                    .stream()
                    .map(v -> v.stream().allMatch(Resultx::getPassed))
                    .collect(Collectors.groupingBy(b -> b, Collectors.counting()))
        );