[英]How to catch exceptions within Java 8 Stream.flatMap(..)
給定一個Stream
和一個返回不同參數的Stream
作為數據源的方法,我正在尋找一種方法來通過flatMap(..)
合並流並在執行過程中捕獲某些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);
}
}
有沒有辦法捕獲由getGenerator(..)
創建的每個單獨的Stream
的Exception
,並簡單地抑制Exception
,用空的Stream
替換“損壞的” Stream
或跳過特定生成器Stream
那些元素?
可以使用Spliterator
將Stream
包裝到另一個Stream
。 此方法將通過捕獲Exception
並保存此狀態來保護給定的Stream
:
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();
一種解決方法是強制在flatMap
方法實現中評估由getGenerator
創建的Stream
。 這會強制在try
- catch
塊中拋出NullPointerException
,因此可以進行處理。
為此,您可以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
更好地進行懶惰評估,這種方法可能會降低性能。
試試這個:
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();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.