java流reduce lambda中的异常处理

e4eetjau  于 2023-04-19  发布在  Java
关注(0)|答案(1)|浏览(149)

我尝试在java中处理流的reduce(item,aggregator)函数中的异常。这是我的原始代码:

List<ReportRow> totalList = newList.stream()
                .collect(Collectors.groupingBy(a -> a.getEngagementCode()))
                .entrySet().stream()
                .map(engagement -> engagement.getValue().stream()
                        .reduce((item, aggregator) -> 
                                new ReportRow(item.getEnvironment(), item.getApplicationName(), item.getEngagementCode(), item.getTotalHits() + aggregator.getTotalHits(), item.getServiceLine(), Float.toString(Float.parseFloat(item.getTotalCost().replace(",", "")) + Float.parseFloat(aggregator.getTotalCost().replace(",", ""))), item.getPrimaryOwnerEmail(), item.getDeploymentId()))
                        .get())
                .collect(Collectors.toList());

这大概就是我所期望的样子(虽然不起作用)。

List<ReportRow> totalList = newList.stream()
                .collect(Collectors.groupingBy(a -> a.getEngagementCode()))
                .entrySet().stream()
                .map(engagement -> engagement.getValue().stream()
                        .reduce((item, aggregator) -> {
                            try {
                                new ReportRow(item.getEnvironment(), item.getApplicationName(), item.getEngagementCode(), item.getTotalHits() + aggregator.getTotalHits(), item.getServiceLine(), Float.toString(Float.parseFloat(item.getTotalCost().replace(",", "")) + Float.parseFloat(aggregator.getTotalCost().replace(",", ""))), item.getPrimaryOwnerEmail(), item.getDeploymentId()))
                            } catch (Exception e) {
                                throw new ImproperDataException("No Deployment Id present in row: "+ item.toString());
                            }
                        }
                        .get())
                .collect(Collectors.toList());

当我试图调用ReportRow()构造函数时,会发生异常。如何处理此异常,同时仍像原始方法中那样使用collect,stream和map?

8cdiaqws

8cdiaqws1#

如果发生未检查的异常,它将终止流处理。这在无法恢复的意外问题的情况下很有用。要做到这一点,ImproperDataException应该扩展RuntimeException。出于可读性原因,我将在方法中移动try-catch和throw逻辑。
对于检查的异常,您应该决定需要发生什么。要么继续该过程(通过跳过该项或使用默认值),要么通过将其 Package 在未检查的异常中来终止。您不能从流表达式传播检查的异常。
下面是一个简化的稍微人为的例子。当发生ArithmeticException时,报告中会跳过costPerHit计算。

static class Value {
    int hits;
    int cost;

    Value(int hits, int cost) {
        this.hits = hits;
        this.cost = cost;
    }
}

static class Report {
    int values;
    int hits;
    int cost;
    double costPerHit;

    Report accumulate(Value value) {
        values++;
        hits += value.hits;
        cost += value.cost;
        updateCostPerHit();
        return this;
    }

    Report combine(Report report) {
        values += report.values;
        hits += report.hits;
        cost += report.cost;
        updateCostPerHit();
        return this;
    }

    private void updateCostPerHit() {
        try {
            costPerHit = cost / hits;
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return String.format("Report{values=%d, hits=%d, cost=%d, costPerHit=%.2f}", values, hits, cost, costPerHit);
    }
}

public static void main(String[] args) {

    List<Value> values = List.of(
        new Value(0, 3), // causes ArithmeticException
        new Value(5, 1),
        new Value(1, 2),
        new Value(3, 8)
    );

    Report output = values.stream().reduce(new Report(), Report::accumulate, Report::combine);

    System.out.println(output);
}

输出:

java.lang.ArithmeticException: / by zero
    at ErrorHandling$Report.updateCostPerHit(ErrorHandling.java:42)
    at ErrorHandling$Report.accumulate(ErrorHandling.java:28)
    at java.base/java.util.stream.ReduceOps$1ReducingSink.accept(ReduceOps.java:80)
    at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:563)
    at interview.streams.ErrorHandling.main(ErrorHandling.java:63)
Report{values=4, hits=9, cost=14, costPerHit=1.00}

实际上,由于我使用单个ReportRow作为各种可变容器,因此使用collect而不是reduce可能更好:

Report output = values.stream().collect(Report::new, Report::accumulate, Report::combine);

我认为这也可能对您的实际代码更好

相关问题