java-break-stream-foreach
Java Stream forEachを中断する方法
1. 概要
Java開発者として、多くの場合、要素のセットを反復処理し、各要素に対して操作を実行するコードを記述します。 link:/java-8-streams-introduction[Java 8ストリームライブラリ]およびその_https://www.baeldung.com/foreach-java [forEach] _メソッドにより、そのコードを記述できます。クリーンで宣言的な方法で。
これはループに似ていますが、**反復を中止するための_break_ステートメントに相当するものがありません*。 *ストリームは非常に長い、または潜在的に無限*である可能性があり、処理を続行する理由がない場合は、最後の要素を待つのではなく、中断します。
このチュートリアルでは、* Stream.forEach_操作で_break_ステートメントをシミュレートできるメカニズムをいくつか見ていきます。*
2. Java 9の_Stream.takeWhile()_
_String_アイテムのストリームがあり、その長さが奇数である限りその要素を処理したいとします。
Java 9 _Stream.takeWhile_メソッドを試してみましょう。
Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck")
.takeWhile(n -> n.length() % 2 != 0)
.forEach(System.out::println);
これを実行すると、出力が得られます。
cat
dog
_for_ループと_break_ステートメントを使用して、これをプレーンJavaの同等のコードと比較して、どのように機能するかを見てみましょう。
List<String> list = asList("cat", "dog", "elephant", "fox", "rabbit", "duck");
for (int i = 0; i < list.size(); i++) {
String item = list.get(i);
if (item.length() % 2 == 0) {
break;
}
System.out.println(item);
}
ご覧のとおり、_takeWhile_メソッドを使用すると、必要なものを正確に実現できます。
しかし、* Java 9をまだ採用していない場合はどうなりますか?* Java 8を使用して同様のことを実現するにはどうすればよいですか?
3. カスタム_Spliterator_
-
Stream.spliterator_のdecoratorとして機能するカスタム_Spliteratorを作成しましょう。 **この_Spliterator_に_break_を実行させることができます。
まず、ストリームから_Spliterator_を取得し、次に_CustomSpliterator_でそれを装飾して、_break_操作を制御する_Predicate_を提供します。 最後に、_CustomSpliterator:_から新しいストリームを作成します
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);
}
_CustomSpliterator_の作成方法を見てみましょう。
public class CustomSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
private Spliterator<T> splitr;
private Predicate<T> predicate;
private 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;
}
}
それでは、_tryAdvance_メソッドを見てみましょう。 ここでは、カスタム_Spliterator_が装飾された_Spliterator_の要素を処理することがわかります。 *処理は、述語が一致し、初期ストリームにまだ要素がある限り実行されます。*条件のいずれかが_false_になると、_Spliterator_ _ "breaks" _およびストリーミング操作が終了します。
新しいヘルパーメソッドをテストしてみましょう。
@Test
public void whenCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned() {
Stream<String> initialStream =
Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck");
List<String> result =
CustomTakeWhile.takeWhile(initialStream, x -> x.length() % 2 != 0)
.collect(Collectors.toList());
assertEquals(asList("cat", "dog"), result);
}
ご覧のとおり、条件が満たされた後、ストリームは停止しました。 テストの目的で、結果をリストに収集しましたが、_forEach_呼び出しまたは_Stream_のその他の関数を使用することもできます。
4. カスタム_forEach_
_break_メカニズムを組み込んだ_Stream_を提供することは有用ですが、_forEach_操作のみに焦点を合わせる方が簡単な場合があります。
デコレータなしで** _ Stream.spliterator _ **を直接使用しましょう:
public class CustomForEach {
public static class Breaker {
private 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);
});
}
}
}
ご覧のように、新しいcustom _forEach_メソッドは、https://www.baeldung.com/java-8-functional-interfaces [_BiConsumer_]を呼び出して、次の要素と、停止に使用できるブレーカーオブジェクトの両方をコードに提供します。ストリーム。
単体テストでこれを試してみましょう。
@Test
public void whenCustomForEachIsCalled_ThenCorrectItemsAreReturned() {
Stream<String> initialStream = Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck");
List<String> result = new ArrayList<>();
CustomForEach.forEach(initialStream, (elem, breaker) -> {
if (elem.length() % 2 == 0) {
breaker.stop();
} else {
result.add(elem);
}
});
assertEquals(asList("cat", "dog"), result);
}
5. 結論
この記事では、ストリームで_break_を呼び出すのと同等の方法を提供しました。 Java 9の_takeWhile_がどのように私たちのためにほとんどの問題を解決するか、そしてそのバージョンをJava 8に提供する方法を見ました。
最後に、_Stream_を繰り返し処理しながら、_break_操作に相当するものを提供できるユーティリティメソッドを調べました。
いつものように、サンプルコードはhttps://github.com/eugenp/tutorials/tree/master/java-streams-2[GitHubで]にあります。