2010-03-21 20 views
67

Ho una lunga serie di confronti da fare in Java, e mi piacerebbe sapere se uno o più di essi risultano veri. La stringa dei confronti era lunga e difficile da leggere, quindi l'ho riorganizzata per renderla leggibile, e sono andato automaticamente a utilizzare un operatore di scorciatoia |= anziché negativeValue = negativeValue || boolean.Collegamento "or-assignment" (| =) operatore in Java

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

mi aspetto negativeValue per essere vero se qualcuno di default <qualcosa> valori sono negativi. È valido? Farà quello che mi aspetto? Non riuscivo a vederlo menzionato sul sito o sullo stackoverflow di Sun, ma Eclipse non sembra avere un problema con esso e il codice viene compilato ed eseguito.


Allo stesso modo, se volevo eseguire diverse intersezioni logiche, potrei usare &= invece di &&?

+13

Perché non lo provi? – Roman

+0

Questa è una logica booleana generale, non solo Java. così puoi cercarlo su altri posti. E perché non lo provi? – Dykam

+14

@Dykam: No, c'è un comportamento specifico qui. Java * potrebbe * scegliere di effettuare | = corto-circuito, in modo che non valuti l'RHS se l'LHS è già vero - ma non lo fa. –

risposta

171

|= è un operatore di assegnazione composto (JLS 15.26.2) per l'operatore logico booleano | (JLS 15.22.2); da non confondere con il condizionale o || (JLS 15.24). Ci sono anche &= e ^= corrispondenti alla versione di assegnazione composta della logica booleana & e ^ rispettivamente.

In altre parole, per boolean b1, b2, questi due sono equivalenti:

b1 |= b2; 
b1 = b1 | b2; 

La differenza fra gli operatori logici (& e |) rispetto alle loro controparti condizionali (&& e ||) è che la prima non lo fanno "corto circuito"; quest'ultimo fa. Cioè:

  • & e |sempre valutare entrambi gli operandi
  • && e || valutano il diritto operando condizionatamente; l'operando di destra viene valutato solo se il suo valore può influire sul risultato dell'operazione binaria.Ciò significa che l'operando di destra non viene valutato quando:
    • L'operando a sinistra di && viene valutato come false
      • (perché non importa quale sia l'operando di destra restituisce, l'intera espressione è false)
    • L'operando sinistro || viene valutato come true
      • (perché nessuno m Atter ciò che l'operando di destra restituisce, l'intera espressione è true)

Quindi, tornando alla tua domanda iniziale, sì, che costrutto è valida, e mentre |= non è esattamente un collegamento equivalente per = e ||, calcola ciò che si desidera. Poiché il lato destro dell'operatore |= nel tuo utilizzo è un'operazione di confronto tra numeri interi, il fatto che | non sia cortocircuitato è insignificante.

Ci sono casi in cui si desidera un cortocircuito o addirittura richiesto, ma lo scenario non è uno di questi.

È spiacevole che a differenza di alcuni altri linguaggi, Java non ha &&= e ||=. Questo è stato discusso nella domanda Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=).

+0

+1, molto approfondito. sembra plausibile che un compilatore possa benissimo convertirsi in un operatore a corto circuito, se potesse determinare che l'RHS non ha effetti collaterali. qualche indizio su questo? – Carl

+0

Ho letto che quando RHS è banale e SC non è necessario, gli operatori SC "intelligenti" sono in realtà un po 'più lenti. Se è vero, allora è più interessante chiedersi se alcuni compilatori possono convertire SC in NSC in determinate circostanze. – polygenelubricants

+0

@polygenelubrificanti Gli operatori in cortocircuito coinvolgono un ramo di qualche tipo sotto la cappa, quindi se non c'è un modello di previsione delle branch-friendly per i valori di verità usati con gli operatori e/o l'architettura in uso non ha una buona/nessuna previsione di ramo (e supponendo che il compilatore e/o la macchina virtuale non facciano alcuna ottimizzazione correlata), allora sì, gli operatori SC introdurranno una certa lentezza rispetto al non cortocircuito. Il modo migliore per scoprire dal compilatore qualsiasi cosa sarebbe compilare un programma usando SC vs. NSC e quindi confrontare il bytecode per vedere se è diverso. – JAB

15

Non è un operatore "di scelta rapida" (o di cortocircuito) nel modo in cui || e & & sono (nel senso che non valuteranno l'RHS se già conoscono il risultato basato sull'LHS) ma farà quello che vuoi in termini di funzionante.

Come esempio della differenza, questo codice sarà bene se text è nullo:

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

che ciò non sarà:

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

(Ovviamente si potrebbe fare "".equals(text) per quel particolare caso: sto solo cercando di dimostrare il principio)

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

Penso che preferirei 'negativeValue = defaultStock <0 || defaultWholesale <0' ecc. A parte l'inefficienza di tutto il pugilato e il wrapping in corso qui, non trovo quasi altrettanto facile capire cosa significherebbe veramente il tuo codice. –

+0

I ha ancora più di 4 parametri e il criterio è lo stesso per tutti, quindi mi piace la mia soluzione, ma per motivi di leggibilità lo separerei in più righe (es. Crea lista, trova valore minimo, confronta valore minimo con 0). – Roman

+0

Sembra utile per un numero elevato di confronti. In questo caso, ritengo che la riduzione della chiarezza non valga il risparmio nel digitare ecc. –

1

Si potrebbe avere solo una dichiarazione. Espressa su più righe che legge quasi esattamente come il vostro codice di esempio, solo meno imperativo:

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

Per le espressioni più semplici, utilizzando | può essere più veloce di || perché, anche se si evita di fare un confronto significa utilizzare un ramo implicitamente e questo può essere molte volte più costoso.

+0

Ho iniziato con uno solo, ma come indicato nella domanda originale ho sentito che" La stringa dei confronti era lunga e difficile da leggere, quindi l'ho riorganizzato per essere leggibile ". A parte questo, in questo caso sono più interessato ad apprendere il comportamento di | = rispetto a far funzionare questo particolare pezzo di codice. –

1

Anche se potrebbe essere eccessivo per il tuo problema, la libreria Guava ha una bella sintassi con Predicate s e esegue la valutazione di cortocircuito di o/e Predicate s.

In sostanza, i confronti vengono trasformati in oggetti, inseriti in una raccolta e quindi ripetuti. Per o predicati, il primo hit vero ritorna dall'iterazione e viceversa per e.

0

|| logico booleano OR
| OR bit a bit

| = bit a bit OR inclusivo e assegnazione operatore

Il motivo per cui | = non shortcircit è perché fa un OR bit a bit non un OR logico. Vale a dire:

 

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

Tutorial for java operators

+0

NON c'è NO bit a bit con booleani! L'operatore '|' è intero bit a bit OR ** ma è anche ** OR logico - vedere [Specifica del linguaggio Java 15.22.2.] (Http://docs.oracle.com/javase/specs/jls/se7/html/ jls-15.html # jls-15.22.2 "JLS 15.22.2") –

+0

Si è corretti perché per un singolo bit (un booleano) OR bit per bit e logico sono equivalenti. Anche se praticamente, il risultato è lo stesso. – oneklc

1

Se si tratta di leggibilità ho il concetto di separazione testato i dati dalla logica di test.Esempio di codice:

Il codice appare più dettagliato e autoesplicativo. Si può anche creare un array in chiamata di metodo, in questo modo:

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

E 'più leggibile di 'stringa di confronto', e ha anche vantaggi prestazionali di corto circuito (a costo di allocazione matrice e chiamata di metodo).

Edit: Ancora più leggibilità può essere ottenuto semplicemente utilizzando varargs parametri:

firma metodo sarebbe:

boolean checkIfAnyNegative(DataType ... data) 

e la chiamata potrebbe assomigliare a questo:

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

L'allocazione di array e una chiamata al metodo sono un costo piuttosto elevato per il cortocircuito, a meno che non si abbiano alcune operazioni costose nei confronti (l'esempio nella domanda è comunque economico). Detto questo, la maggior parte delle volte la manutenibilità del codice supererà le considerazioni sulle prestazioni. Probabilmente userò una cosa del genere se facessi il confronto in modo diverso in un gruppo di luoghi diversi o confrontando più di 4 valori, ma per un singolo caso è un po 'prolisso per i miei gusti. –

+0

@DavidMason Sono d'accordo. Tuttavia, tieni presente che la maggior parte dei calcolatori moderni potrebbe ingoiare quel tipo di sovraccarico in meno di pochi millisecondi. Personalmente non mi preoccuperei di sovraccaricare fino a problemi di prestazioni, che [sembra essere ragionevole] (http://en.wikipedia.org/wiki/Program_optimization#When_to_optimize). Inoltre, la prolissità del codice è un vantaggio, specialmente quando javadoc non viene fornito o generato da JAutodoc. –