怎么break java8 stream的foreach


怎么break java8 stream的foreach

簡介

我們通常需要在java stream中遍歷處理里面的數據,其中foreach是最最常用的方法。

但是有時候我們並不想處理完所有的數據,或者有時候Stream可能非常的長,或者根本就是無限的。

一種方法是先filter出我們需要處理的數據,然后再foreach遍歷。

那么我們如何直接break這個stream呢?今天本文重點講解一下這個問題。

使用Spliterator

上篇文章我們在講Spliterator的時候提到了,在tryAdvance方法中,如果返回false,則Spliterator將會停止處理后續的元素。

通過這個思路,我們可以創建自定義Spliterator。

假如我們有這樣一個stream:

Stream<Integer> ints = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

我們想定義一個操作,當x > 5的時候就停止。

我們定義一個通用的Spliterator:

public class CustomSpliterator<T> extends Spliterators.AbstractSpliterator<T>  {

    private Spliterator<T> splitr;
    private Predicate<T> predicate;
    private volatile boolean isMatched = true;

    public CustomSpliterator(Spliterator<T> splitr, Predicate<T> predicate) {
        super(splitr.estimateSize(), 0);
        this.splitr = splitr;
        this.predicate = predicate;
    }

    @Override
    public synchronized boolean tryAdvance(Consumer<? super T> consumer) {
        boolean hadNext = splitr.tryAdvance(elem -> {
            if (predicate.test(elem) && isMatched) {
                consumer.accept(elem);
            } else {
                isMatched = false;
            }
        });
        return hadNext && isMatched;
    }
}

在上面的類中,predicate是我們將要傳入的判斷條件,我們重寫了tryAdvance,通過將predicate.test(elem)加入判斷條件,從而當條件不滿足的時候返回false.

看下怎么使用:

@Slf4j
public class CustomSpliteratorUsage {

    public static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<T> predicate) {
        CustomSpliterator<T> customSpliterator = new CustomSpliterator<>(stream.spliterator(), predicate);
        return StreamSupport.stream(customSpliterator, false);
    }

    public static void main(String[] args) {
        Stream<Integer> ints = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> result =
          takeWhile(ints, x -> x < 5 )
                        .collect(Collectors.toList());
        log.info(result.toString());
    }
}

我們定義了一個takeWhile方法,接收Stream和predicate條件。

只有當predicate條件滿足的時候才會繼續,我們看下輸出的結果:

[main] INFO com.flydean.CustomSpliteratorUsage - [1, 2, 3, 4]

自定義forEach方法

除了使用Spliterator,我們還可以自定義forEach方法來使用自己的遍歷邏輯:

public class CustomForEach {

    public static class Breaker {
        private volatile boolean shouldBreak = false;

        public void stop() {
            shouldBreak = true;
        }

        boolean get() {
            return shouldBreak;
        }
    }

    public static <T> void forEach(Stream<T> stream, BiConsumer<T, Breaker> consumer) {
        Spliterator<T> spliterator = stream.spliterator();
        boolean hadNext = true;
        Breaker breaker = new Breaker();

        while (hadNext && !breaker.get()) {
            hadNext = spliterator.tryAdvance(elem -> {
                consumer.accept(elem, breaker);
            });
        }
    }
}

上面的例子中,我們在forEach中引入了一個外部變量,通過判斷這個外部變量來決定是否進入spliterator.tryAdvance方法。

看下怎么使用:

@Slf4j
public class CustomForEachUsage {

    public static void main(String[] args) {
        Stream<Integer> ints = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> result = new ArrayList<>();
        CustomForEach.forEach(ints, (elem, breaker) -> {
            if (elem >= 5 ) {
                breaker.stop();
            } else {
                result.add(elem);
            }
        });
        log.info(result.toString());
    }
}

上面我們用新的forEach方法,並通過判斷條件來重置判斷flag,從而達到break stream的目的。

總結

本文通過兩個具體的例子講解了如何break一個stream,希望大家能夠喜歡。

本文的例子https://github.com/ddean2009/learn-java-streams/tree/master/break-stream-foreach

歡迎關注我的公眾號:程序那些事,更多精彩等着您!
更多內容請訪問 www.flydean.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM