Java流(Stream)中的异常处理和lambda表达式。

huangapple go评论68阅读模式
英文:

Exception handling in java stream reduce lambda

问题

I am trying to handle exceptions inside the reduce(item, aggregator) function for a stream in java.
这部分不需要翻译。

This is what my original code looks like:
这是我的原始代码:

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

and this is roughly what I expect it to look like (doesn't work though).
这大致是我期望它看起来像的(但不起作用)。

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

The exception happens when I am trying to call the ReportRow() constructor.
异常发生在我尝试调用ReportRow()构造函数时。

What would be the way to handle this exception while still making using of collect, stream and map as in the original method?
在仍然使用collect、stream和map的情况下,如何处理这个异常呢?

英文:

I am trying to handle exceptions inside the reduce(item,aggregator) function for a stream in java.
This is what my original code looks like:

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

and this is roughly what I expect it to look like (doesn't work though).

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

The exception happens when I am trying to call the ReportRow() constructor.
What would be the way to handle this exception while still making using of collect, stream and map as in the original method ?

答案1

得分: 1

如果发生未经检查的异常,将终止流处理。这在遇到无法恢复的意外问题时很有用。为了实现这一点,ImproperDataException 应该扩展 RuntimeException。出于可读性的原因,我会将 try-catch 和抛出逻辑移到一个方法内。

对于已检查的异常,您应该决定需要发生什么。要么继续处理(跳过该项或使用默认值),要么通过将其包装在未经检查的异常中终止。您不能从流表达式传播已检查的异常。

这是一个简化的、稍微虚构的示例。当出现 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), // 导致 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);

我认为这也可能更适合您的实际代码。

英文:

If an unchecked exception occurs it will terminate the stream processing. This is useful in case of unexpected problems that you cannot recover from. To do this, the ImproperDataException should extend RuntimeException. For readability reasons I would move the try-catch and throw logic inside a method.

For checked exceptions you should decide what needs to happen. Either continue the process (by skipping the item or using a default value) or terminate by wrapping it in an unchecked exception. You cannot propagate checked exceptions from a stream expression.

Here is a simplified and slightly contrived example. When the ArithmeticException occurs, the costPerHit calculation is skipped in the report.

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(&quot;Report{values=%d, hits=%d, cost=%d, costPerHit=%.2f}&quot;, values, hits, cost, costPerHit);
}
}
public static void main(String[] args) {
List&lt;Value&gt; 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);
}

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}

Actually since I am using a single ReportRow as a mutable container of sorts, it's probably better to use collect instead of reduce:

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

I think this might also be better for your actual code

huangapple
  • 本文由 发表于 2023年4月17日 16:39:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76033212.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定