web-dev-qa-db-ja.com

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

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

_streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
_

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

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

peek()を使用してmutateストリーム要素をアンチパターンまたは不適切なアドバイスですか?

編集:

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

_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()を使用すると、オブジェクトのclassと同じであることが一目でわかります。

さらに、peek()を使用すると、変化するラムダを使用できますが、map()を使用すると、トレインレックを構築する必要があります。比較:

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

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

個人的なメモとして、私はpeek()を使用して変更することをためらいません-とても便利だと思います。

42
Bohemian

あなたは正しいです。英語の意味での「覗き見」は「見ますが触れないこと」を意味します。

ただし、JavaDoc の状態:

ピーク

ストリームピーク(消費者アクション)

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

キーワード:「実行中...アクション」と「消費済み」。 JavaDocは、peekにストリームを変更する機能があることを期待する必要があることを非常に明確にしています。

ただし、JavaDocにも次のように記載されています。

API注:

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

これは、それが観察するためのものであることを示しています。ストリーム内のロギング要素。

これらすべてから私が取ったことは、ストリーム内の要素singを実行できるということですが、ストリーム内のmutating要素は回避する必要があります。たとえば、先に進んでオブジェクトのメソッドを呼び出しますが、オブジェクトの操作は変更しないようにします。

少なくとも、これらの行に沿ってコードに簡単なコメントを追加します。

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

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

27
user22815

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

1
Jules