怎麼break java8 stream的foreach

怎麼break java8 stream的foreachjava

簡介

咱們一般須要在java stream中遍歷處理裏面的數據,其中foreach是最最經常使用的方法。git

可是有時候咱們並不想處理完全部的數據,或者有時候Stream可能很是的長,或者根本就是無限的。github

一種方法是先filter出咱們須要處理的數據,而後再foreach遍歷。ide

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

使用Spliterator

上篇文章咱們在講Spliterator的時候提到了,在tryAdvance方法中,若是返回false,則Spliterator將會中止處理後續的元素。code

經過這個思路,咱們能夠建立自定義Spliterator。get

假如咱們有這樣一個stream:it

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

咱們想定義一個操做,當x > 5的時候就中止。java8

咱們定義一個通用的Spliterator:ast

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
相關文章
相關標籤/搜索