Quali sono i casi in cui è meglio usare AND incondizionato (e invece di &&)

Mi piacerebbe conoscere alcuni casi in Java (o più in generale: in programmazione) quando è preferibile nelle espressioni booleane utilizzare l’ AND incondizionato ( & ) invece della versione condizionale ( && ).

So come funzionano, ma non riesco a pensare a un caso quando uso il singolo & vale la pena.

Ho trovato casi nella vita reale in cui entrambi i lati dell’espressione erano davvero a buon mercato, quindi si è rasato via un nanosecondo o due per evitare il ramo e usare l’incondizionato & invece di && . (Si trattava di utility matematiche estremamente performanti, però: non avrei quasi mai usato questo in altri codici, e non avrei comunque dovuto farlo senza un benchmarking esaustivo per dimostrare che era meglio.)

(Per dare esempi specifici, x > 0 sarà super economico e privo di effetti collaterali.Perché preoccuparsi di rischiare un errore di branca per evitare un test che sarà comunque così economico? Certo, dal momento che è un boolean il risultato finale è andrà comunque usato in un ramo, ma if (x >= 0 && x < = 10) coinvolge due rami, e if (x >= 0 & x < = 10) coinvolge solo uno).

L’unica differenza è che && e || interrompere la valutazione non appena è nota. Quindi per esempio:

 if (a != null && a.get() != null) 

funziona bene con && , ma con & potresti ottenere una NullPointerException se a è nullo.

L’unico caso in cui riesco a pensare a dove vuoi usare è se il secondo operando ha un effetto collaterale, ad esempio (probabilmente non è l’esempio migliore ma ottieni il punto):

 public static void main(String[] args) { int i = 1; if (i == 0 & ++i != 2) { } System.out.println(i); //2 i = 1; if (i == 0 && ++i != 2) { } System.out.println(i); //1 } 

Tuttavia, questo mi sembra un codice maleodorante (in entrambi i casi).

Il && consente alla jvm di eseguire la valutazione del cortocircuito. Cioè, se il primo argomento è falso, allora non ha bisogno di preoccuparsi di controllare il secondo argomento.

Un singolo e correrà indipendentemente da entrambi i lati.

Quindi, come esempio forzato, potresti avere:

 if (account.isAllowed() & logAccountAndCheckFlag(account)) // Do something 

In questo esempio, potresti sempre voler registrare il fatto che il proprietario dell’account ha tentato di fare qualcosa.

Non penso di aver mai usato un singolo e in programmazione commerciale però.

Wikipedia ha ben descritto la valutazione del cortocircuito

Dove preferisci gli operatori non a corto circuito?

Dallo stesso link:

  • La seconda condizione non testata porta a un effetto collaterale non corretto
  • Efficienza del codice

Il cortocircuito può portare a errori nella previsione dei rami sui processori moderni e ridurre drasticamente le prestazioni (un esempio notevole è il raggio altamente ottimizzato con il codice di intersezione della casella allineato asse nella tracciatura del raggio) [chiarimento necessario]. Alcuni compilatori possono rilevare tali casi ed emettere un codice più veloce, ma non è sempre ansible a causa di possibili violazioni dello standard C. Il codice altamente ottimizzato dovrebbe utilizzare altri modi per farlo (come l’utilizzo manuale del codice assembly)

Se ci sono effetti collaterali che devono accadere, ma è un po ‘brutto.

AND bit ( & ) è per lo più utile solo per questo: la matematica bit a bit.

La convalida dell’input è un ansible caso. In genere, si desidera segnalare tutti gli errori in un modulo all’utente in un unico passaggio anziché arrestarsi dopo il primo e forzarli a fare clic ripetutamente su Invia e ottenere un singolo errore ogni volta:

 public boolean validateField(string userInput, string paramName) { bool valid; //do validation if (valid) { //updates UI to remove error indicator (if present) reportValid(paramName); } else { //updates UI to indicate a problem (color change, error icon, etc) reportInvalid(paramName); } } public boolean validateAllInput(...) { boolean valid = true; valid = valid & validateField(userInput1, paramName1); valid = valid & validateField(userInput2, paramName2); valid = valid & validateField(userInput3, paramName3); valid = valid & validateField(userInput4, paramName4); valid = valid & validateField(userInput5, paramName5); return valid; } public void onSubmit() { if (validateAllInput(...)) { //go to next page of wizard, update database, etc processUserInput(userInput1, userInput2, ... ); } } public void onInput1Changed() { validateField(input1.Text, paramName1); } public void onInput2Changed() { validateField(input2.Text, paramName2); } ... 

Certo, potresti banalmente evitare la necessità di una valutazione di cortocircuito in validateAllInput() rifattorizzando la logica if (valid) { reportValid() ... al di fuori di validateField() ; ma poi dovresti chiamare il codice estratto ogni volta che è stato chiamato validateField (); come minimo aggiungere 10 linee extra per le chiamate di metodo. Come sempre, è il caso in cui il tradeoff funziona meglio per te.

Se l’espressione è banale, potresti ottenere una micro-ottimizzazione usando & o | in quanto stai impedendo un ramo. vale a dire.

 if(a && b) { } if(!(a || b)) { } 

equivale a

 if (a) if (b) { } if (!a) if (!b) { } 

che ha due posizioni può verificarsi un ramo.

Tuttavia utilizzando un incondizionato & o | , ci può essere solo un ramo.

Whetehr questo aiuta o no è altamente dipendente da ciò che sta facendo il codice.

Se lo usi, ti suggerisco di commentarlo per chiarire perché è stato fatto.

Non esiste un uso specifico di single & ma puoi considerare la seguente situazione.

 if (x > 0 & someMethod(...)) { // code... } 

Si consideri che someMethod() sta eseguendo alcune operazioni che modificheranno le variabili di istanza o eseguiranno qualcosa che influirà sul comportamento successivamente nell’elaborazione.

Quindi in questo caso se usi && operatore e la prima condizione fallisce, non andrà mai in someMethod() . In questo caso, il singolo & operatore saranno sufficienti.

Poiché & è un operatore bit-saggio, è ansible eseguire contemporaneamente fino a 32 controlli in un’unica operazione. Questo può diventare un significativo aumento di velocità per questi casi d’uso molto specifici. Se è necessario verificare un numero elevato di condizioni e farlo spesso e il costo del boxing / unboxing le condizioni vengono ammortizzate dal numero di controlli, o se si memorizzano i dati su disco e su RAM in quel formato ( è più efficiente in termini di spazio per memorizzare 32 condizioni in una singola maschera di bit), l’operatore & può dare un enorme vantaggio di velocità su una serie di 32 singoli && . Ad esempio, se vuoi selezionare tutte le unità che possono muoversi, è una fanteria, ha un potenziamento delle armi ed è controllata dal giocatore 3, puoi fare:

 int MASK = CAN_MOVE | INFANTRY | CAN_ATTACK | HAS_WEAPON_UPGRADE | PLAYER_3; for (Unit u in allunits) { if (u.mask & MASK == MASK) { ...; } } 

Vedi le mie altre risposte su una domanda correlata per ulteriori informazioni sull’argomento.

L’unico vantaggio che posso pensare è quando è necessario richiamare un metodo o eseguire un codice, indipendentemente dalla prima espressione valutata a true o false :

 public boolean update() { // do whatever you want here return true; } // ... if(x == y & update()){ /* ... */} 

Sebbene tu possa farlo senza & :

 if(x == y){/* ... */} update(); 

Il cortocircuito può portare a errori nella previsione dei rami sui processori moderni e ridurre drasticamente le prestazioni (un esempio notevole è il raggio altamente ottimizzato con il codice di intersezione della casella allineato asse nella tracciatura del raggio) [chiarimento necessario].