[解決済み] Java 8 Iterable.forEach() vs foreachループ
質問
Java 8では、次のうちどれがより良いプラクティスでしょうか?
Java 8です。
joins.forEach(join -> mIrc.join(mSession, join));
Java 7です。
for (String join : joins) {
mIrc.join(mSession, join);
}
ラムダで簡略化できるforループがたくさんありますが、ラムダを使うメリットは本当にあるのでしょうか?パフォーマンスや可読性を向上させることができるのでしょうか?
EDIT
また、この質問を長いメソッドに拡張してみます。ラムダから親関数を返したりブレークしたりできないのは知っていて、これも比較する際に考慮すべきですが、他に何かありますか?
どのように解決するのですか?
より良い方法は
for-each
. に違反することに加え
シンプルに、バカに
の原則に反して、この新型の
forEach()
は、少なくとも次のような欠陥がある。
- 非終端変数が使用できない . そのため、以下のようなコードはforEach lambdaにすることができません。
Object prev = null; for(Object curr : list) { if( prev != null ) foo(prev, curr); prev = curr; }
-
チェックした例外を処理できない . ラムダは実際にはチェックされた例外を投げることを禁じられているわけではありません。
Consumer
は宣言していません。したがって、検査される例外を投げるコードはすべてtry-catch
またはThrowables.propagate()
. しかし、そうしたとしても、スローされた例外がどうなるかは、必ずしも明らかではありません。の中のどこかに飲み込まれるかもしれません。forEach()
-
フロー制御の制限 . A
return
はラムダと同じです。continue
に相当するものはありませんが、for-eachではbreak
. また、戻り値やショートサーキット、あるいは セットフラグ (の違反でなければ、少しは緩和されたかもしれません)。 非終端変数なし のルールに従ってください)。 これは単なる最適化ではなく、(ファイルの行を読むような)いくつかのシーケンスに副作用があったり、無限シーケンスを持つ可能性があることを考慮すると、非常に重要です。 -
並列に実行される可能性がある これは、最適化が必要な0.1%のコード以外には、とても恐ろしいことなのです。並列コードはすべて考え抜かなければなりません(ロックやボラタイルなど、従来のマルチスレッド実行の特に厄介な部分を使用しないとしても)。どんなバグでも見つけるのは難しいでしょう。
-
パフォーマンスを低下させる可能性がある なぜなら、JITはforEach()+lambdaをプレーンなループと同じ程度には最適化できないからで、特にlambdaが新しくなった現在ではそうです。最適化とは、ラムダを呼び出すオーバーヘッド(これは小さい)ではなく、最新のJITコンパイラが実行中のコードに対して行う高度な解析と変換のことを指しています。
-
並列処理が必要な場合は、ExecutorService を使用する方がはるかに高速で、難易度もそれほど高くありません。 . ストリームは自動化されている(つまり、あなたの問題についてあまり知らない)。 と は、特殊な (一般的なケースでは非効率的な) 並列化戦略を使用します ( フォークジョイン再帰的分解 ).
-
デバッグをより混乱させる なぜなら、ネストされたコール階層と、禁じられた並列実行があるからです。デバッガは周囲のコードから変数を表示することができず、ステップスルーのようなものが期待通りに動作しない可能性があります。
-
一般的にストリームは、コーディング、読み込み、デバッグが難しい . 実はこれ、複雑な"にも言えることなんです。 流暢 API全般に言えることです。複雑な単一ステートメント、ジェネリックの多用、中間変数の不足などの組み合わせが、混乱したエラーメッセージを生み出し、デバッグを挫折させるのです。このメソッドにはX型のオーバーロードがありません」ではなく、「どこかで型を間違えたようですが、どこでどのように間違えたのかわかりません」というようなエラーメッセージが表示されます。最後に、コードを読んで、実行の各段階での型や動作を理解することは、自明ではないかもしれません。
-
目立つ . Java言語には、すでにfor-each文があります。なぜそれを関数呼び出しに置き換えるのでしょうか?なぜ式のどこかに副作用を隠すことを推奨するのでしょうか?なぜ扱いにくいワンライナーを推奨するのでしょうか?通常のfor-eachと新しいfor-eachを無造作に混ぜるのは悪いスタイルです。コードはイディオム(繰り返しのために理解が早いパターン)で話すべきで、イディオムの数が少ないほどコードは明確になり、どのイディオムを使うかを決めるのに費やす時間が少なくなります(私のような完璧主義者には大きな時間の浪費です!)。
このように、私はforEach()が意味のある場合を除き、あまり好きではありません。
特に私が不快に感じるのは
Stream
は実装されていません。
Iterable
(実際にはメソッドを持つにもかかわらず
iterator
) で、for-each では使用できず、forEach() でしか使用できません。でストリームをイテラブルにキャストすることをお勧めします。
(Iterable<T>)stream::iterator
. より良い代替案は
StreamEx
を実装するなど、Stream API の多くの問題を修正しています。
Iterable
.
それはそうと。
forEach()
は、以下のような場合に有効です。
-
同期リストに対するアトミックな反復処理 . これ以前は
Collections.synchronizedList()
は、get や set のようなものに関してはアトミックでしたが、繰り返し実行する際にはスレッドセーフではありませんでした。 -
並列実行(適切な並列ストリームを使用) . これは、ExecutorService を使用した場合と比較して、数行のコードを節約できます。もし、あなたの問題が Streams や Spliterators に組み込まれた性能の想定と一致するのであれば、この方法を使用することができます。
-
特定のコンテナ 同期リストのように、イテレーションを制御することで利益を得ることができます (ただし、もっと多くの例を挙げてもらわない限り、これはほとんど理論上のことです)。
-
単一の関数をよりクリーンに呼び出す を使用することで
forEach()
とメソッド参照の引数(つまりlist.forEach (obj::someMethod)
). しかし、チェックされた例外、より困難なデバッグ、コードを書くときに使用するイディオムの数を減らすという点には留意してください。
参考にした記事
- Java 8のすべて
- イテレーション・インサイド&アウト (他の投稿者の指摘による)
EDITです。 ラムダに関する当初の提案のいくつか(例えば http://www.javac.info/closures-v06a.html Googleキャッシュ ) は、私が述べた問題のいくつかを解決してくれました(もちろん、独自の複雑さはありますが)。
関連
-
eclipse で「アクセス制限: タイプ 'HttpServer' は API ではありません」というプロンプトが表示される。
-
java Mail send email smtp is not authenticated by TLS encryption solution.
-
Javaエラーメッセージがenclosingクラスでない
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] JavaでNullPointerExceptionを回避する方法
-
[解決済み] JavaにおけるHashMapとHashtableの違いは何ですか?
-
[解決済み] Java Mapの各エントリを効率的に反復処理するには?
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] JavaでArrayListではなくLinkedListを使用するのはいつですか?
-
[解決済み] JavaScriptのオブジェクトをループスルーまたは列挙するにはどうすればよいですか?
最新
-
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 エラー報告 スレッド "main" での例外 java.util.NoSuchElementException
-
Methodのinvokeメソッド実装のJavaリフレクション
-
スレッド "main" での例外 java.lang.ArrayIndexOutOfBoundsException: 1
-
Java コンパイルエラー - スレッド "main" で例外 java.lang.Error: 未解決のコンパイル問題です。
-
起動時にEclipseエラーが発生しました。起動中に内部エラーが発生しました。java.lang.NullPoin: "Javaツーリングの初期化 "中に内部エラーが発生しました。
-
[解決済み] Javaでリストを反復処理する方法
-
[解決済み] Java 8のストリーム内部からCHECKED例外を投げるにはどうすればよいですか?
-
[解決済み】Stream<T>がIterable<T>を実装していないのはなぜですか?
-
[解決済み】Java 8 Lambda関数が例外を投げる?
-
[解決済み] Java 8: ラムダストリーム、例外を含むメソッドによるフィルタリング