2010-03-21 19 views
67

Ich habe eine lange Reihe von Vergleichen in Java zu tun, und ich würde gerne wissen, ob einer oder mehrere von ihnen als wahr herauskommen. Die Reihe der Vergleiche war lang und schwer zu lesen, daher habe ich sie aus Gründen der Lesbarkeit getrennt und ging automatisch zur Verwendung eines Verknüpfungsoperators |= anstelle von negativeValue = negativeValue || boolean.Abkürzung "or-assignment" (| =) Operator in Java

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

Ich erwarte, dass negativeValue um wahr zu sein, wenn eine der Standard <etwas> Werte negativ sind. Ist das gültig? Wird es tun, was ich erwarte? Ich konnte es nicht auf Suns Website oder Stackoverflow erwähnt sehen, aber Eclipse scheint kein Problem damit zu haben und der Code kompiliert und läuft.


Und falls ich mehrere logische Schnittstellen durchführen wollte, könnte ich verwenden &= statt &&?

+13

Warum versuchst du es nicht? – Roman

+0

Dies ist eine allgemeine boolesche Logik, nicht nur Java. damit Sie es an anderen Orten nachschlagen können. Und warum versuchst du es nicht einfach? – Dykam

+14

@Dykam: Nein, hier gibt es ein spezifisches Verhalten. Java * könnte * wählen, dass | = kurzgeschlossen wird, so dass es die RHS nicht auswertet, wenn die LHS bereits wahr ist - aber das tut es nicht. –

Antwort

171

Die |= ist ein zusammengesetzter Zuweisungsoperator (JLS 15.26.2) für den booleschen logischen Operator | (JLS 15.22.2); nicht mit dem Conditional-oder || (JLS 15.24) zu verwechseln. Es gibt auch &= und ^=, die der zusammengesetzten Zuweisungsversion der booleschen logischen & bzw. ^ entsprechen.

Mit anderen Worten, für boolean b1, b2, sind diese zwei gleichwertige:

b1 |= b2; 
b1 = b1 | b2; 

Der Unterschied zwischen den logischen Operatoren (& und |) im Vergleich zu ihren bedingten Pendants (&& und ||) ist, dass erstere nicht "Kurzschluss"; Letzteres tun. Das heißt:

  • & und |immer bewerten beide Operanden
  • && und || den rechten Operanden bedingt bewerten; Der rechte Operand wird nur ausgewertet, wenn sein Wert das Ergebnis der binären Operation beeinflussen könnte.Das bedeutet, dass der rechte Operand nicht ausgewertet wird, wenn:
    • Der linke Operand von &&-false wertet
      • (denn egal, was der rechte Operand ausgewertet, der gesamte Ausdruck ist false)
    • linker Operand || ausgewertet true
      • (weil kein m Atter, was der rechte Operand ausgewertet, der gesamte Ausdruck ist true)

Also auf Ihre ursprüngliche Frage zurück, ja, ist das Konstrukt gültig, und während |= ist nicht gerade eine äquivalente Abkürzung für = und || berechnet es, was Sie wollen. Da die rechte Seite des |=-Operators in Ihrer Verwendung eine einfache ganzzahlige Vergleichsoperation ist, ist die Tatsache, dass | nicht kurzschließt, unbedeutend.

Es gibt Fälle, in denen ein Kurzschluss erwünscht oder sogar erforderlich ist, aber Ihr Szenario gehört nicht dazu.

Es ist bedauerlich, dass Java im Gegensatz zu einigen anderen Sprachen nicht &&= und ||= hat. Dies wurde in der Frage Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=) diskutiert.

+0

+1, sehr gründlich. Es erscheint plausibel, dass ein Compiler sich gut in einen Kurzschlussoperator umwandeln könnte, wenn er feststellen könnte, dass die RHS keine Nebenwirkungen hat. irgendeinen Hinweis darauf? – Carl

+0

Ich lese, dass, wenn RHS trivial ist und SC nicht notwendig ist, die "intelligenten" SC-Operatoren tatsächlich ein bisschen langsamer sind. Wenn dies zutrifft, ist es interessanter, sich zu fragen, ob einige Compiler SC unter bestimmten Umständen in NSC konvertieren können. – polygenelubricants

+0

@polygeneLubricants Kurzgeschlossene Operatoren beinhalten eine Verzweigung irgendeiner Art unter der Haube, wenn also kein verzweigungsvorhersagefreundliches Muster zur Wahrheit existiert, haben Werte, die mit den Operatoren und/oder der verwendeten Architektur verwendet werden, keine gute Verzweigungsvorhersage (Und vorausgesetzt, der Compiler und/oder die virtuelle Maschine führen selbst keine entsprechenden Optimierungen durch), dann werden die SC-Operatoren eine gewisse Langsamkeit im Vergleich zum Nicht-Kurzschluss einführen. Der beste Weg, um aus dem Compiler herauszufinden, wäre, ein Programm mit SC vs. NSC zu kompilieren und dann den Bytecode zu vergleichen, um zu sehen, ob er sich unterscheidet. – JAB

15

Es ist kein "Shortcut" -Operator in der Art, dass || und & & sind (in, dass sie die RHS nicht auswerten werden, wenn sie bereits das Ergebnis basierend auf der LHS kennen), aber es wird tun, was Sie in Bezug auf arbeiten wollen.

Als Beispiel für den Unterschied, wird dieser Code in Ordnung sein, wenn text ist null:

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

Erwägung, dass dies nicht:

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

(Offensichtlich Sie "".equals(text) für diesen bestimmte tun könnten Fall - ich versuche nur, das Prinzip zu demonstrieren.)

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

Ich denke, ich würde 'negativeValue = defaultStock <0 || bevorzugen defaultGroßhandel <0' usw. Neben der Ineffizienz des gesamten Verpackens und Verpackens finde ich es nicht annähernd so einfach zu verstehen, was dein Code wirklich bedeuten würde. –

+0

I er hat sogar mehr als 4 Parameter und das Kriterium ist das gleiche für alle, dann mag ich meine Lösung, aber aus Gründen der Lesbarkeit würde ich es in mehrere Zeilen trennen (dh Liste erstellen, minvalue finden, minvalue mit 0 vergleichen). – Roman

+0

Das sieht für eine große Anzahl von Vergleichen nützlich aus. In diesem Fall denke ich, dass die Verringerung der Klarheit die Einsparung beim Tippen usw. nicht wert ist. –

1

Sie könnten nur eine Aussage haben. mehrere Linien überexprimiert es fast genau wie Ihr Beispielcode liest, nur weniger zwingend notwendig:

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

Für einfachste Ausdrücke, | verwendet, kann schneller als || sein, denn auch wenn es einen Vergleich vermeidet tun es bedeutet, mit einem Zweig implizit und Das kann viel teurer sein.

+0

ist Ich begann mit nur einem, aber wie in der ursprünglichen Frage gesagt, fühlte ich, dass "Die Reihe der Vergleiche war lang und schwer zu lesen, also habe ich es zur besseren Lesbarkeit abgebrochen ". Abgesehen davon bin ich in diesem Fall mehr daran interessiert, das Verhalten von | = zu lernen, als dieses bestimmte Stück Code zu arbeiten. –

1

Obwohl es für Ihr Problem vielleicht zuviel kostet, hat die Guava Bibliothek eine nette Syntax mit Predicate s und führt eine Kurzschlussauswertung von/und Predicate s durch.

Im Wesentlichen werden die Vergleiche in Objekte umgewandelt, in eine Sammlung verpackt und dann iteriert. Für oder Prädikate kehrt der erste wahre Treffer von der Iteration zurück und umgekehrt für und.

0

|| logischer Boolscher OR
| bitweise OR

| = bitweise inklusive OR und Zuweisungsoperator

Der Grund, warum | = shortcircit nicht ist, weil es eine bitweise tut oder nicht eine logische ODER-Verknüpfung. Das heißt:

 

C |= 2 is same as C = C | 2 

Tutorial for java operators

+0

gibt es kein bitweises ODER mit Booleans! Der Operator '|' ist ein bitweises Integer-ODER **, ist aber auch ein logisches ODER - siehe [Java Language Specification 15.22.2.] (Http://docs.oracle.com/javase/specs/jls/se7/html/ jls-15.html # jls-15.22.2 "JLS 15.22.2") –

+0

Sie haben Recht, weil für ein einzelnes Bit (ein boolescher) bitweises und logisches ODER äquivalent sind. Obwohl das Ergebnis praktisch dasselbe ist. – oneklc

1

Wenn es um Lesbarkeit ich das Konzept der Trennung von Daten aus der Testlogik getestet haben.Codebeispiel:

// 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; 
} 

Der Code sieht ausführlicher und selbsterklärend aus. Sie können sogar ein Array in Methodenaufruf wie folgt erstellen:

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

es besser lesbar als ‚Vergleichsstring‘ ist, und hat auch Performance-Vorteil von Kurzschlüssen (auf Kosten der Array-Zuweisung und Methodenaufruf).

Edit: Noch mehr Lesbarkeit kann einfach durch die Verwendung varargs Parameter erreicht werden: wäre

Methode Unterschrift:

boolean checkIfAnyNegative(DataType ... data) 

Und der Aufruf wie folgt aussehen könnte:

checkIfAnyNegative(defaultStock, defaultWholesale, defaultRetail, defaultDelivery); 
+1

Array-Zuweisung und ein Methodenaufruf sind ziemlich große Kosten für den Kurzschluss, es sei denn, Sie haben einige teure Operationen in den Vergleichen (das Beispiel in der Frage ist jedoch billig). Abgesehen davon wird die Wartbarkeit des Codes in den meisten Fällen Leistungsüberlegungen übertrumpfen. Ich würde wahrscheinlich so etwas verwenden, wenn ich den Vergleich an verschiedenen Orten anders mache oder mehr als 4 Werte vergleiche, aber für einen einzelnen Fall ist es für meinen Geschmack etwas ausführlich. –

+0

@DavidMason Ich stimme zu. Bedenken Sie jedoch, dass die meisten modernen Rechner diese Art von Overhead in weniger als ein paar Millis verschlingen würden. Persönlich würde ich mich nicht um Overhead kümmern, bis Leistungsprobleme, die [sinnvoll] scheint (http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize). Außerdem ist Code-Ausführlichkeit ein Vorteil, insbesondere wenn Javadoc nicht von JAutodoc bereitgestellt oder generiert wird. –