peek()を使用してストリーム要素を変更するのはアンチパターンですか?


37

モノのストリームがあり、それらを途中で「強化」したい場合peek()、これを行うために使用できます。たとえば:

streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

コードのこの時点でthingMutatorモノを変更することは正しい動作であると仮定します。たとえば、メソッドは「lastProcessed」フィールドを現在の時間に設定できます。

ただし、peek()ほとんどのコンテキストでは、「見て、触れないでください」という意味です。

ストリーム要素の突然変異にアンチパターンを使用peek()ていますか?

編集:

より一般的な代替アプローチは、消費者を変換することです。

private void thingMutator(Thing thing) {
    thing.setLastProcessed(System.currentTimeMillis());
}

パラメータを返す関数に:

private Thing thingMutator(Thing thing) {
    thing.setLastProcessed(currentTimeMillis());
    return thing;
}

map()代わりに使用します:

stream.map(this::thingMutator)...

しかし、それは機能的なコード(return)を導入しpeek()、同じオブジェクトを返すことを知っているので、それがより明確であるとは確信していませんがmap()、同じクラスのオブジェクトであることは一目でさえわかりません。

さらに、peek()あなたは突然変異するラムダを持つことができmap()ますが、列車の残骸を構築する必要があります。比較する:

stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)

私は思うpeek()ので、何の「神秘的」副作用はありません、バージョンが明確で、かつラムダは明らかに変異されます。同様に、メソッド参照が使用され、メソッドの名前が明らかに突然変異を暗示している場合、それも明確で明白です。

個人的には、peek()突然変異に使うことをためらわない-非常に便利だと思う。



1
peek要素を動的に生成するストリームで使用しましたか?それでも機能しますか、それとも変更は失われますか?ストリームの要素を変更することは私には信頼できません。
セバスチャンレッド

1
@SebastianRedl 100%信頼できることがわかりました。最初に処理中に収集するために使用しましたList<Thing> list; things.stream().peek(list::add).forEach(...);。非常に便利です。最近。公開用の情報を追加するために使用しました:Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());。この例を実行する他の方法があることは知っていますが、ここでは非常に単純化しすぎています。を使用peek()すると、よりコンパクトでエレガントなコードが生成されます。読みやすさは別として、この質問はあなたが何を提起したかに関するものです。安全/信頼できますか?
ボヘミアン

2
Javaストリームの質問は、デバッグ専用です。同様に興味深いかもしれません。
ビンセントサバード

@ボヘミアン。あなたはまだこのアプローチを実践していpeekますか?stackoverflowについても同様の質問がありますので、それを見てフィードバックをお寄せください。ありがとう。stackoverflow.com/questions/47356992/...
tsolakp

回答:


23

あなたは正しいです、英語の意味での「覗く」は「見て、触れないで」という意味です。

ただしJavaDocの状態:

ピーク

ストリームピーク(コンシューマアクション)

このストリームの要素で構成されるストリームを返します。さらに、結果のストリームから要素が消費されると、各要素で指定されたアクションを実行します。

キーワード:「実行中...アクション」および「消費済み」。JavaDocにはpeek、ストリームを変更する機能があることを期待すべきであることは非常に明確です。

ただし、JavaDocには次のことも記載されています。

API注:

このメソッドは、主にデバッグをサポートするために存在します。ここでは、パイプラインの特定のポイントを通過する要素を表示します。

これは、ストリーム内の要素をログに記録するなど、監視することを目的としていることを示しています。

このすべてから私が取るのは、ストリーム内の要素を使用してアクション実行できることですが、ストリーム内の要素を変更することは避けるべきです。たとえば、先に進んでオブジェクトのメソッドを呼び出しますが、オブジェクトの操作を変更しないようにします。

少なくとも、次の行に沿ってコードに短いコメントを追加します。

// Note: this peek() call will modify the Things in the stream.
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

このようなコメントの有用性に関する意見は異なりますが、この場合はこのようなコメントを使用します。


1
メソッド名が明確に変異、たとえばthingMutator、より具体的なものresetLastProcessedなどを示す場合、なぜコメントが必要なのか。説得力のある理由がない限り、提案などの必要なコメントは通常、変数名やメソッド名の選択が不適切であることを示しています。良い名前が選択された場合、それだけでは十分ではありませんか?それとも、良い名前にもかかわらず、peek()ほとんどのプログラマーが「スキム」する(視覚的にスキップする)盲点のようなものだと言っていますか?また、「主にデバッグ用」は「デバッグ専用」と同じものではありません-「主に」使用する以外の用途は何ですか?
ボヘミアン

@Bohemianメソッド名が直感的でないため、主に説明します。そして、私はOracleで働いていません。私は彼らのJavaチームにはいません。私は彼らが何を意図したのか分かりません。

@Bohemianは、要素を変更する方法を探しているとき、「ピーク」は何も変更しないため、「ピーク」で行の読み取りを停止するのが好きではないからです。後で、コードの他のすべての場所に追跡しているバグが含まれていない場合にのみ、これに戻って、おそらくデバッガで武装し、「omg、誰がこの誤解を招くコードを置いたのか!」
フランクホプキンス

すべてがとにかく上の読み取りに1行、1人のニーズにですが、1は「PEEK」の内容をスキップする可能性があり、多くの場合、ストリームの文は、1行に1つのストリーム操作で複数行の上に行くここexampeで@Bohemian
フランク・ホプキンス

1

簡単に誤解される可能性があるため、このような使用は避けます。おそらく最良のオプションは、ラムダ関数を使用して、必要な2つのアクションをforEach呼び出しにマージすることです。また、既存のオブジェクトを変更するのではなく、新しいオブジェクトを返すことを検討することもできます-効率はわずかに低下する可能性がありますが、コードが読みやすくなり、変更されたリストを誤って使用する可能性を減らす可能性がありますオリジナルを受け取りました。


-2

APIノートは、メソッドが主にデバッグ/ロギング/発火統計などのアクションのために追加されたことを示しています。

@apiNoteこのメソッドは、主にデバッグをサポートするために存在します。パイプライン内の特定のポイントを通過するエレメントを表示する場合は、次のようにします。

       {@コード
     * Stream.of( "one"、 "two"、 "three"、 "four")
     * .filter(e-> e.length()> 3)
     * .peek(e-> System.out.println( "フィルターされた値:" + e))
     * .map(String :: toUpperCase)
     * .peek(e-> System.out.println( "マップされた値:" + e))
     * .collect(Collectors.toList());
     *}


2
あなたの答えは、すでにスノーマンのものです。
ビンセントサバード

3
そして、「主に」は「唯一」を意味するものではありません。
ボヘミアン

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.