takeWhile() が flatmap と異なる動作をする件
質問
takeWhileの可能性を探るために、takeWhileを使ったスニペットを作成しています。flatMapと組み合わせて使用した場合、動作が期待通りになりません。以下のコードスニペットをご覧ください。
String[][] strArray = {{"Sample1", "Sample2"}, {"Sample3", "Sample4", "Sample5"}};
Arrays.stream(strArray)
.flatMap(indStream -> Arrays.stream(indStream))
.takeWhile(ele -> !ele.equalsIgnoreCase("Sample4"))
.forEach(ele -> System.out.println(ele));
実際の出力
Sample1
Sample2
Sample3
Sample5
ExpectedOutputです。
Sample1
Sample2
Sample3
期待する理由は、takeWhileは中の条件がtrueになるまで実行される必要があるからです。また、デバッグのために、flatmapの中にprintout文を追加しています。ストリームは2回だけ返され、これは期待に沿ったものです。
しかし、これはチェーンにflatmapがなくても問題なく動作します。
String[] strArraySingle = {"Sample3", "Sample4", "Sample5"};
Arrays.stream(strArraySingle)
.takeWhile(ele -> !ele.equalsIgnoreCase("Sample4"))
.forEach(ele -> System.out.println(ele));
実際の出力
Sample3
ここでは、実際の出力と期待される出力が一致しています。
免責事項:これらのスニペットはコードの練習用であり、有効なユースケースを提供するものではありません。
更新しました。
バグ
JDK-8193856
: 修正は JDK 10 の一部として利用可能になる予定です。
この変更により
whileOps
Sink::accept
@Override
public void accept(T t) {
if (take = predicate.test(t)) {
downstream.accept(t);
}
}
実装を変更しました。
@Override
public void accept(T t) {
if (take && (take = predicate.test(t))) {
downstream.accept(t);
}
}
どのように解決するのですか?
これは JDK 9 のバグです。 問題番号 8193856 :
<ブロッククオート
takeWhile
は、上流のオペレーションがキャンセルをサポートし、それを尊重することを誤って仮定しています。
flatMap
.
説明
ストリームが順序付けされている場合。
takeWhile
は期待された動作を示すはずです。これはあなたのコードでは完全にそうなっているわけではありません。
forEach
を使っているからです。この例ではそうなっていますが、もし気になるようでしたら
forEachOrdered
を使うべきです。面白いことに、これでは何も変わりません。????
ということは、そもそもストリームは順序付けされていないのでは?(その場合
の動作は大丈夫です。
.) から作成されたストリームに対して一時変数を作成すると
strArray
という式を実行し、それが順序付けされているかどうかをチェックする。
((StatefulOp) stream).isOrdered();
をブレークポイントで実行すると、確かに順序付けされていることがわかります。
String[][] strArray = {{"Sample1", "Sample2"}, {"Sample3", "Sample4", "Sample5"}};
Stream<String> stream = Arrays.stream(strArray)
.flatMap(indStream -> Arrays.stream(indStream))
.takeWhile(ele -> !ele.equalsIgnoreCase("Sample4"));
// breakpoint here
System.out.println(stream);
つまり、これは実装上のエラーである可能性が非常に高いということです。
コードの中へ
他の人が疑っているように、私も今、この
は
につながっています。
flatMap
が熱心であることと関係があるかもしれません。より正確には、両方の問題が同じ根本原因を持っているかもしれません。
の原因を調べてみると
WhileOps
のソースを見ると、これらのメソッドを見ることができます。
@Override
public void accept(T t) {
if (take = predicate.test(t)) {
downstream.accept(t);
}
}
@Override
public boolean cancellationRequested() {
return !take || downstream.cancellationRequested();
}
このコードは
takeWhile
で指定されたストリーム要素をチェックするために
t
が存在するかどうかを調べます。
predicate
が満たされているかどうか。
-
もしそうなら、その要素を
downstream
操作、この場合はSystem.out::println
. -
そうでない場合は
take
を false に設定し、次回パイプラインをキャンセルするかどうか (つまり終了するかどうか) を尋ねられたときにtrue
.
これは
takeWhile
の操作になります。他に知っておかなければならないのは
forEachOrdered
というメソッドを実行する末端のオペレーションを導くことです。
ReferencePipeline::forEachWithCancel
:
@Override
final boolean forEachWithCancel(Spliterator<P_OUT> spliterator, Sink<P_OUT> sink) {
boolean cancelled;
do { } while (
!(cancelled = sink.cancellationRequested())
&& spliterator.tryAdvance(sink));
return cancelled;
}
これがやっていることは
- パイプラインがキャンセルされたかどうかをチェックする
- そうでなければ、シンクを1要素進める
- 最後の要素であった場合は停止する
期待できそうでしょう?
なし
flatMap
良い場合(flatMap
がない場合(2番目の例)
forEachWithCancel
を直接操作して
WhileOp
として
sink
として、どのように再生されるかを見ることができます。
-
ReferencePipeline::forEachWithCancel
はループを行う。-
WhileOps::accept
は各ストリーム要素 -
WhileOps::cancellationRequested
は各要素の後にクエリされます
-
-
ある時点で
"Sample4"
は述語に失敗し、ストリームはキャンセルされます。
やったーーーー
と
flatMap
悪い場合(
flatMap
;あなたの最初の例)。
forEachWithCancel
は
flatMap
の操作を行うが、これは単に
forEachRemaining
の上に
ArraySpliterator
に対して
{"Sample3", "Sample4", "Sample5"}
というようにします。
if ((a = array).length >= (hi = fence) &&
(i = index) >= 0 && i < (index = hi)) {
do { action.accept((T)a[i]); } while (++i < hi);
}
をすべて無視し
hi
と
fence
のようなもので、並列ストリームのために配列処理を分割する場合にのみ使用されるもので、これは単純な
for
ループで、各要素を
takeWhile
演算に渡す。
に渡しますが、それがキャンセルされたかどうかをチェックすることはありません。
. そのため、停止する前に、その "substream" のすべての要素を熱心に調べ、おそらくは
ストリームの残りを通して
.
関連
-
XMLファイル操作時のjava.util.NoSuchElementExceptionを解決する方法。
-
ajax コミット リソースの読み込みに失敗しました: サーバーはステータス 400 で応答しました ()
-
[解決済み] Mavenを使用して、依存関係を持つ実行可能なJARを作成するにはどうすればよいですか?
-
[解決済み] 特定のUnicode文字を含むコメントでのJavaコードの実行が許可されているのはなぜですか?
-
[解決済み] Java 8のmap()メソッドとflatMap()メソッドの違いは何ですか?
-
[解決済み] Distinct() with lambda?
-
[解決済み] Java 8でインデックスを持つストリームを反復処理する簡潔な方法はありますか?
-
[解決済み] javaストリームで整数のリストを合計する方法は?
-
[解決済み】Collectors.toMapでNullPointerExceptionが発生する。
-
[解決済み】Java 8のOptionalをStream::flatMapで使用する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
javaの非静的メソッドを静的に参照することができない
-
Solve モジュールのビルドに失敗しました。Error: ENOENT: no such file or directory エラー
-
Javaクラスが "Error occurred during initialization of boot layer "というエラーで実行される。
-
ApplicationContextの起動エラーです。条件レポートを表示するには、アプリケーションを'de'で再実行します。
-
StringBuilderが投げるArrayIndexOutOfBoundsExceptionの探索
-
が 'X-Frame-Options' を 'sameorigin' に設定したため、フレーム内に存在する。
-
CertificateException: XXXに一致するサブジェクトの代替DNS名が見つかりません 解決策
-
java send https request prompt java.security.cert.について。
-
htmlとwordの相互変換の実装(画像あり)
-
JavaストリームでflatMap()の後のfilter()が「完全には」遅延しない理由とは?