簡體   English   中英

如何在Java 8 Stream.flatMap(..)中捕獲異常

[英]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(..)創建的每個單獨的StreamException ,並簡單地抑制Exception ,用空的Stream替換“損壞的” Stream或跳過特定生成器Stream那些元素?

可以使用SpliteratorStream包裝到另一個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM