给定一个Stream
和一个为不同的参数返回Stream
作为数据源的方法,我正在寻找一种通过flatMap(..)
合并流并捕获某些{{ 1}}。
让我们看下面的代码片段:
Exceptions
是否有任何方法可以捕获由public class FlatMap {
public static void main(final String[] args) {
long count;
// this might throw an exception
count = Stream.of(0.2, 0.5, 0.99).flatMap(chance -> getGenerator(chance, 20)).count();
// trying to catch the exception in flatMap() will not work
count = Stream.of(0.2, 0.5, 0.99).flatMap(chance -> {
try {
return getGenerator(chance, 20);
} catch (final NullPointerException e) {
return Stream.empty();
}
}).count();
System.out.println(count);
}
// !! we cannot change this method, we simply get a Stream
static Stream<Object> getGenerator(final double chance, final long limit) {
return Stream.generate(() -> {
if (Math.random() < chance) return new Object();
throw new NullPointerException();
}).limit(limit);
}
}
创建的每个单独Stream
的异常,并简单地抑制getGenerator(..)
,将“损坏的” Exception
替换为是空的还是跳过特定生成器Stream
中的那些元素?
答案 0 :(得分:2)
可以使用Stream
将Spliterator
包装到另一个。此方法通过捕获Stream
并保存此状态来保护给定的Exception
:
static <T> Stream<T> protect(final Stream<T> stream) {
final Spliterator<T> spliterator = stream.spliterator();
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE,
spliterator.characteristics() & ~Spliterator.SIZED) {
private boolean corrupted = false;
@Override
public boolean tryAdvance(final Consumer<? super T> action) {
if (!corrupted) try {
return spliterator.tryAdvance(action);
} catch (final Exception e) {
// we suppress this one, stream ends here
corrupted = true;
}
return false;
}
}, false);
}
然后,我们可以包装Stream
方法并安全地将其传递到flatMap(..)
中:
// we protect the stream by a wrapper Stream
count = Stream.of(0.2, 0.5, 0.99)
.flatMap(chance -> protect(getGenerator(chance, 20)))
.count();
答案 1 :(得分:2)
一种解决方法是强制由Stream
创建的getGenerator
在flatMap
方法实现中进行评估。这迫使NullPointerException
被抛出到try
-catch
块中,因此可以被处理。
为此,您可以collect
Stream
(例如List
):
getGenerator(chance, 20).collect(Collectors.toList()).stream()
将其合并到您的原始代码段中:
public class FlatMap {
public static void main(final String[] args) {
long count;
// trying to catch the exception in flatMap() will not work
count = Stream.of(0.2, 0.5, 0.99)
.flatMap(chance -> {
try {
return getGenerator(chance, 20).collect(Collectors.toList()).stream();
}
catch (final NullPointerException e) {
return Stream.empty();
}
})
.count();
System.out.println(count);
}
// !! we cannot change this method, we simply get a Stream
static Stream<Object> getGenerator(final double chance, final long limit) {
return Stream.generate(() -> {
if (Math.random() < chance) return new Object();
throw new NullPointerException();
}).limit(limit);
}
}
警告:如果getGenerator
Stream
可以更好地进行惰性评估,则此方法可能会降低性能。
答案 2 :(得分:0)
尝试一下:
static <T> Supplier<T> getOrNull(Supplier<T> supplier) {
return () -> {
try {
return supplier.get();
} catch (Throwable e) {
return null;
}
};
}
static Stream<Object> getGenerator(final double chance, final long limit) {
return Stream.generate(
getOrNull(
() -> {
if (Math.random() < chance) return new Object();
throw new NullPointerException();
// You can throw any exception here
}))
.limit(limit)
.filter(Objects::isNull);
}
然后只需调用getGenerator
:
count = Stream.of(0.2, 0.5, 0.99)
.flatMap(chance -> getGenerator(chance, 20))
.count();