Javaのショートカット「or-assignment」(| =)演算子


89

私はJavaで実行する比較の長いセットを持っています、そしてそれらの1つ以上が本当であるかどうか知りたいのですが。比較の文字列は長くて読みにくいので、読みやすくするために分割し、|=ではなく自動的にショートカット演算子を使用しましたnegativeValue = negativeValue || boolean

boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

negativeValuedefault <something>値のいずれかが負の場合、私はtrueになると期待しています。これは有効ですか?それは私が期待することをしますか?Sunのサイトやstackoverflowで言及されているのを確認できませんでしたが、Eclipseには問題がないようで、コードがコンパイルされて実行されます。


同様に、いくつかの論理的な交差を実行したい場合、&=代わりに使用でき&&ますか?


13
やってみませんか?
ローマ

これはJavaだけではなく、一般的なブール論理です。他の場所で調べることができます。そして、それを試してみませんか?
Dykam

16
@Dykam:いいえ、ここには特定の動作があります。Java 、LHSがすでに真である場合、RHSを評価しないように、| =短絡にすることを選択できますが、そうではありません。
Jon Skeet、2010年

2
@Jon Skeet:短絡は存在しない||=演算子に適しています|=が、ビット単位のor演算子の組み合わせ形式です。
David Thornley、2010年

1
@ジョンスキート:確かに、しかし作る|=ため、短絡は、他の複合代入演算子と矛盾だろうa |= b;と同じではありませんa = a | b;評価に関する通常の注意点と、a二回(それが重要な場合)。大きな言語行動の決定にはなかった||=ように思えるので、私はあなたの要点を逃しています。
David Thornley、2010年

回答:


204

|=複合代入演算子(あるJLS 15.26.2ブール論理演算子)|JLS 15.22.2)。conditional-or ||JLS 15.24)と混同しないでください。そこでもある&=^=ブール論理の化合物割り当てバージョンに対応&し、^それぞれ。

つまり、の場合boolean b1, b2、これら2つは同等です。

 b1 |= b2;
 b1 = b1 | b2;

論理演算子(差&及び|それらの条件対応物と比較して)(&&とは||)前者は「短絡」しないことです。後者は行います。あれは:

  • &そして| 常に両方のオペランドを評価する
  • &&そして||右のオペランドを評価する条件付き。右側のオペランドは、その値がバイナリ演算の結果に影響を与える可能性がある場合にのみ評価されます。つまり、次の場合、正しいオペランドは評価されません。
    • の左のオペランドは次のように&&評価されますfalse
      • (正しいオペランドが何に評価されるかに関係なく、式全体はfalse
    • の左のオペランドは次のように||評価されますtrue
      • (正しいオペランドが何に評価されるかに関係なく、式全体はtrue

あなたの元の質問に戻って、はい、その構造は有効であり、一方では|=正確と同等のショートカットではありません=し、||それはあなたが何をしたい計算を行い、。|=使用法の演算子の右側は単純な整数比較演算な|ので、短絡しないという事実は重要ではありません。

短絡が必要な場合や必要な場合さえありますが、シナリオはその1つではありません。

他の一部の言語とは異なり、Javaには&&=and がありません||=。これは、なぜJavaには条件付きAND演算子と条件付きOR演算子の複合代入バージョンがないのですか?(&& =、|| =)


+1、非常に徹底的。RHSに副作用がないと判断できれば、コンパイラが短絡演算子に変換する可能性が高いと考えられます。それについての手がかりは?
カール

私は、RHSが簡単でSCが不要な場合、「スマート」SCオペレーターは実際には少し遅いと読みました。trueの場合、特定の状況下で一部のコンパイラーがSCをNSCに変換できるかどうか疑問に思う方が興味深いでしょう。
polygenelubricants 2010年

@polygenelubricantsの短絡演算子には、内部で何らかの分岐が含まれているため、演算子で使用される真理値に対する分岐予測に適したパターンがない場合や、使用中のアーキテクチャで分岐予測が適切でない場合(コンパイラーや仮想マシンが関連する最適化を行わないことを前提としています)。そうです。SCオペレーターは、非短絡と比較していくらか遅くなります。コンパイラーを見つける最善の方法は、SCとNSCを使用してプログラムをコンパイルし、バイトコードを比較して異なるかどうかを確認することです。
JAB 2014年

また、ビット単位のOR演算子であると混同しないように |
user253751

16

||は、「ショートカット」(または短絡)演算子ではありません。&&は(LHSに基づく結果がすでにわかっている場合はRHSを評価しないため)ですが、動作に関してはあなたが望むことを行います。

違いの例として、このコードtextはnullの場合に問題ありません。

boolean nullOrEmpty = text == null || text.equals("")

一方、これはしません:

boolean nullOrEmpty = false;
nullOrEmpty |= text == null;
nullOrEmpty |= text.equals(""); // Throws exception if text is null

(明らかに、あなたは"".equals(text)その特定のケースのためにあなたがすることができました-私はただ原理を実証しようとしています。)


3

あなたはただ一つのステートメントを持つことができます。複数行で表現されているため、サンプルコードとほとんど同じように読み取れますが、それほど重要ではありません。

boolean negativeValue
    = defaultStock < 0 
    | defaultWholesale < 0
    | defaultRetail < 0
    | defaultDelivery < 0;

最も単純な式の場合、比較を行わなくてもブランチを暗黙的に使用することを意味するため、|よりも高速に使用できます||


最初は1つだけでしたが、元の質問で述べたように、「比較の文字列は長くて読みにくいので、読みやすくするために分割しました」と感じました。それはさておき、この場合、この特定のコードを機能させるよりも、| =の動作を学ぶことに興味があります。
David Mason、

1

あなたの問題にはやり過ぎかもしれませんが、GuavaライブラリーにはPredicatesを使用した構文がいくつかあり、or / and Predicateの短絡評価を行います。

基本的に、比較はオブジェクトに変換され、コレクションにパッケージ化されてから繰り返されます。or述語の場合、最初の真のヒットは反復から返され、andの場合はその逆になります。


1

読みやすさに関するものであれば、テストされたデータをテストロジックから分離するという概念があります。コードサンプル:

// declare data
DataType [] dataToTest = new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
}

// define logic
boolean checkIfAnyNegative(DataType [] data) {
    boolean negativeValue = false;
    int i = 0;
    while (!negativeValue && i < data.length) {
        negativeValue = data[i++] < 0;
    }
    return negativeValue;
}

コードはより冗長で自明です。次のように、メソッド呼び出しで配列を作成することもできます。

checkIfAnyNegative(new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
});

「比較文字列」よりも読みやすく、また、(配列の割り当てとメソッドの呼び出しを犠牲にして)短絡というパフォーマンス上の利点があります。

編集: varargsパラメーターを使用することで、さらに読みやすくすることができます。

メソッドの署名は次のようになります。

boolean checkIfAnyNegative(DataType ... data)

呼び出しは次のようになります。

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

1
配列の割り当てとメソッドの呼び出しは、比較で高価な演算を行わない限り(問題の例は安価ですが)、短絡にかなりのコストがかかります。そうは言っても、ほとんどの場合、コードの保守性はパフォーマンスの考慮事項よりも優先されます。さまざまな場所でさまざまに比較したり、4つ以上の値を比較したりする場合は、おそらくこのようなものを使用しますが、1つのケースでは、私の好みにはやや冗長です。
David Mason

@DavidMason同意する。ただし、最近のほとんどの計算機は、このようなオーバーヘッドを数ミリ未満で吸収することを覚えておいてください。個人的には、パフォーマンスの問題が発生するまでオーバーヘッドを気にしませんでした。また、コードの冗長性は、特にjavadocがJAutodocによって提供または生成されない場合に有利です。
KrzysztofJabłoński2013

1

古い投稿ですが、初心者に別の見方をするために例を挙げておきます。

同様の複合演算子の最も一般的な使用例はだと思います+=。私たちは皆、このようなものを書いたと確信しています:

int a = 10;   // a = 10
a += 5;   // a = 15

これのポイントは何でしたか?ポイントは、ボイラープレートを避け、繰り返しコードを排除することでした。

したがって、次の行はまったく同じことをb1行い、同じ行に変数を2回入力することを避けます。

b1 |= b2;

1
特に名前が長い場合、操作と演算子名の間違いを見つけるのが難しくなります。 longNameOfAccumulatorAVariable += 5;longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;
Andrei

0
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;

私は私が好むと思うnegativeValue = defaultStock < 0 || defaultWholesale < 0ここで起こってすべてのボクシング・包装の非効率性から別になどを、私はあなたのコードが本当に意味するであろうことを理解することはほぼ簡単としてそれを見つけることができません。
Jon Skeet、2010年

私は彼が4つ以上のパラメーターを持っているので、基準はすべてのパラメーターで同じですが、私のソリューションが好きですが、読みやすくするために、数行に分けます(つまり、リストを作成し、minvalueを見つけ、minvalueを0と比較します)。 。
Roman

これは、多数の比較に役立ちます。この場合、明快さを減らすことはタイピングなどの節約に値しないと思います
David Mason

0

|| 論理ブールOR
| ビットごとのOR

| =ビット単位の包含的ORおよび代入演算子

| =が省略されない理由は、論理ORではなくビットORを行うためです。つまり、

C | = 2はC = C |と同じ 2

Javaオペレーターのチュートリアル


ブール値によるビット単位のORはありません!演算子|は整数ビット単位OR ですが、論理OR でもあります-Java言語仕様15.22.2を
user85421 2013年

正解です。単一ビット(ブール)の場合、ビットごとの論理ORは同等です。実際には、結果は同じです。
oneklc 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.