if条件付きでのset.add()のブール戻り値?


15

セットクラスのadd演算子は、ブール値を返します。ブール値は、追加する要素がまだ存在しない場合はtrue、そうでない場合はfalseです。書いています

if (set.add(entry)) {
    //do some more stuff
}

きれいなコードを書くという点で良いスタイルと考えられていますか?あなたは一度に2つのことをするので、私は疑問に思っています。1)要素を追加し、2)要素が存在したかどうかを確認します。


6
あなたは、要素がまだそこになかっjava.util.Setaddときにtrueを返す標準について話しているのですか?
user2357112は、Monica

1
私は通常、反対のテストを検討しますif (!set.add(entry)) {// entry already present, possibly a case you want to handle}
。– njzk2

一度に2つのことを行うと何か問題がありますか?
-user207421

@ user2357112はい、そうです。
アンドレアスブラウン

回答:


19

はい、そうです。

オペレーションがブール値を返す通常のポイントは、それを使用して、つまり構造内で決定を下せることifです。この可能性を実現できる唯一の他の方法は、戻り値を変数に格納し、すぐにそれを再利用ifすることif(operation()) { ... }です。だから、先に進んでそれをしてください、私たちはあなたを判断しません(そのため)。


ご回答有難うございます。@Niels vanReijmersdalが彼の答えで指摘しているように、これを行うとコマンドとクエリの分離が壊れますよね?それは重要だと思いませんか?
アンドレアスブラウン

4
@AndreasBraun個人的には、コマンドクエリの分離は馬鹿げていると思います。多くの場合、1つのメソッドがアクションを実行し、そのアクションに関連する値を返すことは論理的です。2つの間を人為的に分離することは、不必要に冗長なコードにつながります。

1
@ dan1111:確かに。さらに、「コマンドとクエリの分離」は、「チェックアンドアクト」および類似のアンチパターンにつながり、マルチスレッドコンテキストで破損する可能性があります。他の世界が堅牢かつ効率的なSMPソリューションのための原子融合操作を同時に構築しようとしているとき、そのような分離を宣伝するのはかなり奇妙です…
ホルガー

2
@JörgW Mittag:759ページ アトミック操作は本質的に「チェックしてから行動する」アンチパターンに対して免疫があるため、構築するスレッドを安全にする方法を見つけるために「章全体」を読むために分割することは有益ではありません再び。実際の引数に名前を付けていないので、「これらの引数に対する特定の異論」はありません。
ホルガー

1
@JörgW Mittag:さて、私はこのページで答えについて話しているSet.add、代替を命名せずに単一の操作として使用することを思いとどまらせる。目的の操作が単一の要素を追加し、追加されたかどうかを確認することである場合、操作を複雑にするような強力な代替手段は必要ありません。これSet.addは、700ページ以上の書籍を最初に学習しなくても使用できる組み込みのJREメソッドであることに注意してください。
ホルガー

12

メンテナーが戻り値が意味するものをすでに知っているか調べることを強制するため、可能な限りクリーンではないと言います。値がすでに存在していた、まだ存在していなかった、正常に挿入されたことを意味しますか?あなたがそれをあまり使わないなら、あなたは知りません、そして、たとえあなたがそれをするとしても、それははるかに精神的な負荷です。

私は次を好むでしょう:

boolean added = set.add(entry);

if (added) {
    //do some more stuff
}

はい、少し冗長ですが、コンパイラはまったく同じバイトコードを生成する必要があります。Javaセットを何年も使用していない人でも、何も調べずにロジックに従うことができます。



9
メソッドの検索は、使用されるスコープの外側で宣言された冗長変数に対する元の開発者の意図を理解しようとするよりも負担が少ないです。最新のすべてのJava IDEでは、開発者はマウスポインターをメソッドの上に置いてすぐにそのドキュメントにアクセスできます。優れた開発者は、なじみのないAPIを使用する場合、常にこれを実行します。
ケビンクルムウィーデ

9
私は二番目に@Holger。わからない場合はSet.add、経験が浅いので、これらの方法を調べて学習し、経験を積む必要があります。
モニカの

7
notAlreadyPresent最高の言葉遣いではありません。私はを使用しadded、値がセットに追加されない理由を読者が知っていることを期待します。
-njzk2

3
@JustinTime:コメントを使用してコードの機能を説明することは、悪い習慣と広く見なされています。コードは自己記述的でなければなりません。コードレビューでは、許容できないものとしてフラグを立てます。
BlueRaja-ダニーPflughoeft

8

trueが成功を意味する場合、それは良い、明確なコードです。

成功すると、関数またはメソッドがtrue(またはtrueと評価されるもの)を返すという一般的な規則があります。コードがそれに従う限り、条件にメソッドを入れることは問題ないと思います。

私の見解では、このようなコードは不必要に乱雑です:

boolean frobulate_succeeded = thing.frobulate();

if (frobulate_succeeded) {
    ...
}

自分を繰り返しているように感じます。

ただし、戻り値の意味についての質問はあいまいです。「追加された要素が既に存在しているかどうかを示すブール値」と言います。これは、trueが要素が存在したことを意味する可能性があります(追加は発生しませんでした)。その場合は、メソッドの戻り動作をより一般的なものに変更するのが理想的です。それが不可能な場合は、コードで返された結果に明確にラベルを付けることができる追加の中間変数を追加します(他の人が提案したとおり)。


セットにアイテムを追加するという文脈において、成功とはどういう意味ですか?例外をスローしないことは成功を意味します。trueとfalseは、このコンテキストでより具体的なものを意味します。
モニカの

@ Solomonoff'sSecretこの場合、例外とは、セットが要素の追加を拒否したこと、要素falseが既に含まれているために追加されなかったこと、およびtrue要素がまだ含まれていないことを意味します。As add()は、セットにまだ要素が含まれていない場合に要素をセットに追加するtrueため、要素がセットに正常に追加されたことを意味します。
ジャスティンタイム-モニカの復活

@JustinTime 2つのケースに独自の価値判断を追加しています。別の解釈は、成功は、メソッドが戻るときにアイテムがセット内にあるということです。アイテムが既にセットに含まれていた場合、add何もせずに成功します。アイテムがまだセットにない場合は、セットにaddアイテムを追加して成功します。どの解釈が正しいかは任意です。成功のあまりarbitrary意的でない定義は、言語の定義です。メソッドが正常に戻る場合、メソッドは成功します。
モニカの

1
成功の最もarbitrary意的な定義は、メソッドが実行するように設定したタスクを正常に実行したということです。methodがありfirstLessThanSecond(int l, int r)trueif l > rまたはfalseifを返す場合l <= r、そのメソッドは正常に戻りますが成功しません
ジャスティンタイム-モニカの復活

1
@JustinTime addは、契約の厳密な省略形です。ドキュメントは「指定された要素がこのセットにまだ存在しない場合は追加します」で始まります。これは、アイテムがすでにセットに含まれているかどうかに関係なく満たされます。戻り値は自由に解釈できますが、最終的には単なる解釈にすぎません。2番目の例では、メソッドは条件が真であるかどうかを評価します。条件が偽の場合、条件を正常に評価したため、メソッドは確実に失敗していません。
モニカ

2

私はそれが非常にCのようなものだと思います。たいていの場合、突然変異の結果には記述的に名前を付けた変数を使用し、if条件内で突然変異は発生しないようにします。

コンパイラは、すぐに再利用される場合、この変数を削除します。人間はソースを読むのが簡単になります。私にとって、それはより重要です。

if条件にand/ or句を追加して条件を拡張する必要.add()がある場合、短絡評価のために特定のケースで呼び出しを行わないことがあります。短絡が特に予想されない限り、これはバグになる可能性があります。


私が書いた場合if (set.contains(entry)){set.add(entry); //do more stuff}、それもコンパイラによって排除されますか?
アンドレアスブラウン

1
@AndreasBraun:そうは思いません。コンパイラはcontainsとの間のセマンティックリンクについて何も知りませんadd。また、entryセットを検索するという二重の作業を行います。これは、非常に大きなセットと非常に軽いループで役割を果たす可能性があります。
9000

1
それで、私はあなたが書くのではなくif (set.contains(entry)){set.add(entry); //do more stuff}、代わりに例えばカール・ビーレフェルトの答えで行くと思いますか?
アンドレアスブラウン

@AndreasBraun:正確に(その答えを支持しました)。
9000

1
良い点。将来の開発者は他の短絡状態に陥る可能性があるため、ifの突然変異は扱いにくい
-njzk2

0

あなたのコードはCommand Query Separationを壊しているようです。これについては、クリーンコードブック関数構造のビデオで説明しています。クリーンコードの観点からは、良いスタイルとは見なされないと思います。

「クリーンなコードはシンプルで直接的です。きれいなコードは、よく書かれた散文のように読みます。クリーンなコードは、デザイナーの意図を曖昧にすることはありませんが、むしろ鮮明な抽象化と簡単な制御ラインでいっぱいです。

-アプリケーション指向のオブジェクト指向分析および設計のGrady Booch著者」

私にとって、あなたのコードの意図は不明です。エントリが正常に追加されたとき、または既に存在するときにも両方が実行されますか?何がadd()戻りますか?アイテム?エラーコード?


2
まあ、はい、それは私も考えていました。しかし、@ Kilian Fothにもポイントがあると思います。クエリとコマンドを分離したい場合は、ちょっとif (set.contains(entry)){set.add(entry); //do more stuff}馬鹿げているように書かなければなりません。これについてどう思いますか?
アンドレアスブラウン

良い名前を示唆するためにこのコードのドメインとコンテキストを知りませんが、明確な意図を持ってこれを関数にラップします。たとえば、doesEntryExistOrCreateEntry(entry)、contains()+ add()ロジックを含み、ブール値を返すことができます。同様の質問がここにあります:softwareengineering.stackexchange.com/questions/149708/…クリーンコード以外のこのプラクティスについて説明します。
ニールスファンライマースダール

6
コマンドクエリ分離ルールは、特にメソッドがアクションを実行し、アクションの成功状態を返すこのようなケースに適用された場合、愚かだと思います。しかし、また、ルールが何であるかを説明したり、それを正当化する方法で多くを提供したりしません。これらの理由でダウン投票。

1
「add()は何を返しますか?」コードレビューを行うJava開発者であれば、CollectionAPI を知っていることを期待しています。
njzk2

3
@ dan1111と合意しました。それは特に馬鹿げています。なぜなら、それは種族の条件にとって肥沃な土を作るようなものだからです。
ポールドレイパー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.