mem*_*und 7 java java-8 java-stream
stream.map(obj -> doMap(obj)).collect(Collectors.toList());
private String doMap(Object obj) {
if (objectIsInvalid) {
throw new ParseException("Object could not be parsed");
}
}
Run Code Online (Sandbox Code Playgroud)
问题:如何抛出异常并使流迭代知道它不应该破坏整个迭代,而是继续下一个元素(并最终记录失败的对象)?
这是您可以用来改进异常处理的一种怪异技巧。
假设您的mapper函数是这样的:
String doMap(Object obj) {
if (isInvalid(obj)) {
throw new IllegalArgumentException("info about obj");
} else {
return obj.toString();
}
}
Run Code Online (Sandbox Code Playgroud)
如果对象有效,则返回结果,但如果对象无效,则引发异常。不幸的是,如果直接将其粘贴到管道中,则任何错误都会停止管道执行。您想要的是一种类似“两种”类型的东西,可以容纳一个值或一个错误指示符(这在Java中是一个例外)。
事实证明,它CompletableFuture可以包含值或异常。尽管它是用于异步处理的(此处未发生),但我们只需对其进行扭曲即可使用。
首先,给定stream要处理的对象,我们调用包装在的调用中的映射函数supplyAsync:
CompletableFuture<String>[] cfArray =
stream.map(obj -> CompletableFuture.supplyAsync(() -> doMap(obj), Runnable::run))
.toArray(n -> (CompletableFuture<String>[])new CompletableFuture<?>[n]);
Run Code Online (Sandbox Code Playgroud)
(不幸的是,通用数组的创建给出了未经检查的警告,必须将其取消。)
奇数构造
CompletableFuture.supplyAsync(supplier, Runnable::run)
Run Code Online (Sandbox Code Playgroud)
在提供的Executor上“异步”运行供应商,该Executor Runnable::run只是在该线程中立即运行任务。换句话说,它同步运行供应商。
诀窍是,CompletableFuture从该调用返回的实例包含来自供应商的值(如果它正常返回),或者包含异常(如果供应商抛出一个异常)。(我在这里不考虑取消。)然后,我们将CompletableFuture实例收集到一个数组中。为什么要数组?下一部分的设置:
CompletableFuture.allOf(cfArray).join();
Run Code Online (Sandbox Code Playgroud)
这通常等待CF阵列完成。由于它们已经同步运行,因此它们应该已经全部完成。对于这种情况,重要的是,如果阵列中的任何 CF已异常完成,join()将抛出a 。如果连接正常完成,我们可以简单地收集返回值。如果联接引发异常,则可以传播它,也可以捕获它并处理存储在数组中CF中的异常。例如,CompletionException
try {
CompletableFuture.allOf(cfArray).join();
// no errors
return Arrays.stream(cfArray)
.map(CompletableFuture::join)
.collect(toList());
} catch (CompletionException ce) {
long errcount =
Arrays.stream(cfArray)
.filter(CompletableFuture::isCompletedExceptionally)
.count();
System.out.println("errcount = " + errcount);
return Collections.emptyList();
}
Run Code Online (Sandbox Code Playgroud)
如果所有操作均成功,则返回值列表。如果有任何例外,它将计算例外数量并返回一个空列表。当然,您可以轻松地执行其他操作,例如记录异常的详细信息,过滤掉异常并返回有效值列表等。
无一例外你都可以使用Optional:
stream.map(obj -> doMap(obj))
.filter(obj -> obj.isPresent())
.collect(Collectors.toList());
private Optional<String> doMap(Object obj) {
if (objectIsInvalid) {
return Optional.empty();
}
}
Run Code Online (Sandbox Code Playgroud)