怎么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