[解決済み] Java8ストリームの要素を既存のListに追加する方法
質問
コレクタのJavadoc は、ストリームの要素を新しいリストに収集する方法を示しています。既存のArrayListに結果を追加するワンライナーはないのでしょうか?
どのように解決するのですか?
注意事項
nosidの回答
を使用して、既存のコレクションに追加する方法を示しています。
forEachOrdered()
. これは、既存のコレクションを変異させるための便利で効果的なテクニックです。私の答えは、なぜ
Collector
を使用して、既存のコレクションを変更することができます。
簡単に言うと
いいえ
少なくとも、一般的には
Collector
を使用して、既存のコレクションを変更することができます。
コレクターは、スレッドセーフでないコレクションに対しても、並列処理をサポートするように設計されているからだ。その方法は、各スレッドが中間結果のコレクションに対して独立して動作するようにすることである。各スレッドが独自のコレクションを取得する方法は、スレッドセーフのために
Collector.supplier()
を返すことが要求される。
新しい
コレクションを毎回作成します。
これらの中間結果のコレクションは、1つの結果のコレクションになるまで、再びスレッドで制限された方法でマージされる。これが
collect()
演算を行う。
からの回答がいくつかあります。
バルダー
と
アシリャス
を使用することを提案しています。
Collectors.toCollection()
で、新しいリストではなく、既存のリストを返すサプライヤーを渡します。これは、毎回新しい空のコレクションを返すというサプライヤの要件に違反する。
これは、彼らの回答にある例が示すように、単純なケースではうまくいくでしょう。しかし、特にストリームが並列に実行されている場合は、失敗するでしょう。(ライブラリの将来のバージョンでは、予期せぬ方法で変更され、順次実行の場合であっても失敗する可能性があります)。
簡単な例で説明しましょう。
List<String> destList = new ArrayList<>(Arrays.asList("foo"));
List<String> newList = Arrays.asList("0", "1", "2", "3", "4", "5");
newList.parallelStream()
.collect(Collectors.toCollection(() -> destList));
System.out.println(destList);
このプログラムを実行すると、よく
ArrayIndexOutOfBoundsException
. に対して複数のスレッドが動作しているためです。
ArrayList
というスレッドセーフでないデータ構造です。よし、同期化しよう。
List<String> destList =
Collections.synchronizedList(new ArrayList<>(Arrays.asList("foo")));
これで、例外が発生して失敗することはなくなりました。しかし、期待通りの結果ではなく
[foo, 0, 1, 2, 3]
を実行すると、次のような変な結果になります。
[foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0, foo, 2, 3, foo, 2, 3, 1, 0]
これは、上で説明したスレッドに限定した蓄積・合算操作の結果です。並列ストリームでは、各スレッドがサプライヤーを呼び出して、中間蓄積のための独自のコレクションを取得します。を返すサプライヤを渡すと、そのサプライヤは 同じ コレクションを作成すると、各スレッドはその結果をそのコレクションに追加します。スレッド間の順序付けがないので、結果は任意の順序で追加されることになる。
そして、これらの中間コレクションがマージされるとき、これは基本的にリストと自分自身をマージする。リストのマージは
List.addAll()
これは、操作中にソース・コレクションが変更された場合、結果が不定になることを意味します。この場合
ArrayList.addAll()
は配列のコピー操作を行うので、結局は自分自身を複製することになり、これはある意味予想されたことだと思います。(他のListの実装では全く異なる挙動をする可能性があることに注意してください)。いずれにせよ、これで出力先での奇妙な結果や重複する要素の説明がつきます。
ストリームをシーケンシャルに実行するようにしよう」といって、次のようなコードを書くかもしれません。
stream.collect(Collectors.toCollection(() -> existingList))
のように、とにかく このようなことはしないことをお勧めします。ストリームを制御すれば、確かに、並列に実行されないことを保証できます。今後は、コレクションの代わりにストリームを渡すようなプログラミングのスタイルが出現すると思うんだ。ストリームを渡されて、このコードを使っても、そのストリームがたまたま並列であった場合は失敗します。さらに悪いことに、誰かがあなたに順次的なストリームを渡すかもしれない。そして、ある任意の時間後に、システムの他の場所でコードが並列ストリームを使うように変更されるかもしれません。 あなたの のコードが壊れる。
OK、では忘れずに
sequential()
を、このコードを使用する前に任意のストリーム上で実行します。
stream.sequential().collect(Collectors.toCollection(() -> existingList))
もちろん、毎回忘れずに行いますよね:-) 仮にそうしたとしましょう。そうすると、パフォーマンス・チームは、慎重に作られた並列実装がなぜスピードアップしないのか不思議に思うでしょう。そしてまた、その原因を突き止めるのです。 あなたの のコードで、ストリーム全体を順次実行するように強制しています。
やめておけよ。
関連
-
SocketTimeoutExceptionの解決方法です。読み込みがタイムアウトした
-
Maven Pluginの実行がライフサイクル設定の対象外であるエラーの解決
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] Java 8 List<V> を Map<K, V> に変換する。
-
[解決済み] Java 8 StreamをArrayに変換する方法は?
-
[解決済み] Java 8でリストのリストをリストにするにはどうしたらいいですか?
-
[解決済み] 可能な限り常にパラレルストリームを使用した方がいいのでしょうか?
-
[解決済み] IteratorをStreamに変換するには?
-
[解決済み] Java 8 JDK を使用して、Iterable を Stream に変換する
-
[解決済み】ストリームを使用して、オブジェクトのリストをtoStringメソッドから取得した文字列に変換する
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
final, finally, finalizeの違いについて話してください。
-
スキャナは、タイプに解決することはできません最もルーキー初心者の質問
-
JavaMailのメール送信が失敗するケースとその説明の分析
-
Eclipseでプロジェクトエクスプローラービューとパッケージエクスプローラービューを使う
-
スレッド "main" で例外発生 java.lang.ArrayIndexOutOfBoundsException: 0 at One1.main(One1.java:3)
-
Java コンパイルエラー - スレッド "main" で例外 java.lang.Error: 未解決のコンパイル問題です。
-
Javaエラーメッセージがenclosingクラスでない
-
SocketTimeoutExceptionです。読み込みがタイムアウトしました
-
Google Chromeのエラー「Not allowed to load local resource」の解決策について
-
[解決済み] Java 8でリストのリストをリストにするにはどうしたらいいですか?