&& (AND) i || (OR) w instrukcjach IF

137

Mam następujący kod:

if(!partialHits.get(req_nr).containsKey(z) || partialHits.get(req_nr).get(z) < tmpmap.get(z)){  
    partialHits.get(z).put(z, tmpmap.get(z));  
}

gdzie partialHitsjest HashMap.
Co się stanie, jeśli pierwsze stwierdzenie jest prawdziwe? Czy Java nadal sprawdzi drugą instrukcję? Ponieważ aby pierwsza instrukcja była prawdziwa, HashMap nie powinien zawierać podanego klucza, więc jeśli druga instrukcja jest zaznaczona, otrzymam NullPointerException.
Więc w prostych słowach, jeśli mamy następujący kod

if(a && b)  
if(a || b)

czy Java sprawdzi, bczy aw pierwszym przypadku jest fałszywe, a czy ajest prawdziwe w drugim?

Azymut
źródło

Odpowiedzi:

202

Nie, to nie będzie oceniane. I to jest bardzo przydatne. Na przykład, jeśli chcesz sprawdzić, czy ciąg nie ma wartości null lub jest pusty, możesz napisać:

if (str != null && !str.isEmpty()) {
  doSomethingWith(str.charAt(0));
}

Albo na odwrót

if (str == null || str.isEmpty()) {
  complainAboutUnusableString();
} else {
  doSomethingWith(str.charAt(0));
}

Gdybyśmy nie mieli „zwarć” w Javie, otrzymalibyśmy wiele wyjątków NullPointerExceptions w powyższych wierszach kodu.

Andreas Dolk
źródło
Czy istnieją porównania bitowe, aby można było ocenić oba wyrażenia? tj. jeśli (str! = null | str.isEmpty ())? (oczywiście nie jest to praktyczny przykład, w rzeczywistości jest głupi, ale masz pomysł)
Kezzer
5
Dopóki wyrażenia nie mają skutków ubocznych, semantyka zwarć jest logicznie równoważna pełnej ocenie. To znaczy, jeśli A jest prawdziwe, wiesz, że A || B jest prawdziwe bez konieczności oceniania B. Jedyny moment, w którym będzie to miało znaczenie, to sytuacja, gdy wyrażenie ma skutki uboczne. Jeśli chodzi o inne operatory, możesz użyć *i +jako logicznego andi or; ((A?1:0) * (B?1:0)) == 1, ((A?1:0) + (B?1:0)) > 0. Można nawet zrobić xor: ((A?1:0) + (B?1:0)) == 1.
outis
1
@Kezzer: czy to naprawdę porównanie bitowe? Myślę, że jest to booleanoperator (logiczny). Jest inny niż bitwiseoperator (liczba całkowita), mimo że ma ten sam symbol ...
user85421
4
Poręczna sztuczka, gdy chcesz przełączać się między „&&” a „||” wyrażenia to zanegowanie całego wyrażenia w taki sposób, że: !(str != null && !str.isEmpty()) staje się: (str !(!=) null !(&&) !(!)str.isEmpty()) a potem: (str == null || str.isEmpty()) ponieważ: !(!=) is == !(&&) is || !(!) eliminates itself inne pomocne negacje to: !(<) is >= !(>) is <= i
viceversa
68

Java ma 5 różnych logicznych operatorów porównania: &, &&, |, ||, ^

& i && są operatorami „i”, | i || „lub”, ^ to „xor”

Pojedyncze sprawdzą każdy parametr, niezależnie od wartości, przed sprawdzeniem wartości parametrów. Podwójne najpierw sprawdzą lewy parametr i jego wartość, a jeśli true( ||) lub false( &&) pozostawi drugi nietknięty. Skompilowany dźwięk? Prosty przykład powinien wyjaśnić:

Podano dla wszystkich przykładów:

 String aString = null;

I:

 if (aString != null & aString.equals("lala"))

Oba parametry są sprawdzane przed wykonaniem oceny, a dla drugiego parametru zostanie zgłoszony wyjątek NullPointerException.

 if (aString != null && aString.equals("lala"))

Pierwszy parametr jest sprawdzany i zwraca false, więc drugi parametr nie będzie sprawdzany, ponieważ wynik i falsetak jest .

To samo dotyczy OR:

 if (aString == null | !aString.equals("lala"))

Podniesie również NullPointerException.

 if (aString == null || !aString.equals("lala"))

Pierwszy parametr jest sprawdzany i zwraca true, więc drugi parametr nie będzie sprawdzany, ponieważ wynik i truetak jest .

XOR nie może zostać zoptymalizowany, ponieważ zależy to od obu parametrów.

Mocno zakodowane
źródło
3
„Java ma 4 różne logiczne operatory porównania: &, &&, |, ||” ... Zapominasz ^(xor).
aioobe
Och, nie zdawałem sobie sprawy, że sprawdza też wartości boolowskie. Do tej pory używał go tylko do masek bitowych.
Zakodowane
27

Nie, to nie będzie sprawdzane. Takie zachowanie nazywane jest oceną zwarć i jest funkcją w wielu językach, w tym w Javie.

Peter van der Heijden
źródło
20

Wszystkie odpowiedzi tutaj są świetne, ale aby zilustrować, skąd się to bierze, w przypadku takich pytań dobrze jest udać się do źródła: specyfikacji języka Java.

Sekcja 15:23, Operator warunkowy i (&&) , mówi:

Operator && jest podobny do & (§15.22.2), ale oblicza jego prawostronny operand tylko wtedy, gdy wartość jego lewego operandu jest prawdziwa. [...] W czasie wykonywania lewe wyrażenie operandu jest oceniane jako pierwsze [...] jeśli wynikowa wartość jest fałszywa, wartość warunkowego i wyrażenia jest fałszywa, a prawostronne wyrażenie operandu nie jest oceniane . Jeśli wartość operandu po lewej stronie jest prawdziwa, wówczas wartość wyrażenia po prawej stronie jest [...] obliczana, a wynikowa wartość staje się wartością wyrażenia warunkowego i wyrażenia. Zatem && oblicza ten sam wynik co & na operandach boolowskich. Różni się tylko tym, że wyrażenie operandu po prawej stronie jest oceniane warunkowo, a nie zawsze.

I podobnie, sekcja 15:24, operator warunkowego lub (||) , mówi:

|| operator jest podobny do | (§15.22.2), ale ocenia jego prawostronny operand tylko wtedy, gdy wartość jego lewego operandu jest fałszywa. [...] W czasie wykonywania, wyrażenie po lewej stronie jest obliczane jako pierwsze; [...] jeśli otrzymana wartość jest prawdziwa, wartość wyrażenia warunkowego lub wyrażenia warunkowego jest prawdziwa, a prawostronne wyrażenie operandowe nie jest obliczane. Jeśli wartością argumentu po lewej stronie jest fałsz, obliczane jest wyrażenie po prawej stronie; [...] wynikowa wartość staje się wartością wyrażenia warunkowego. Zatem || oblicza ten sam wynik co | na operandach boolowskich lub boolowskich. Różni się tylko tym, że wyrażenie operandu po prawej stronie jest oceniane warunkowo, a nie zawsze.

Może trochę powtarzalne, ale najlepsze potwierdzenie tego, jak dokładnie działają. Podobnie operator warunkowy (? :) oblicza tylko odpowiednią „połowę” (lewa połowa, jeśli wartość jest prawdą, prawa połowa, jeśli jest fałszywa), pozwalając na użycie wyrażeń takich jak:

int x = (y == null) ? 0 : y.getFoo();

bez wyjątku NullPointerException.

Cowan
źródło
6

Nie, jeśli a jest prawdziwe (w orteście), b nie będzie testowane, ponieważ wynik testu będzie zawsze prawdziwy, niezależnie od wartości wyrażenia b.

Zrób prosty test:

if (true || ((String) null).equals("foobar")) {
    ...
}

będzie nie rzucać NullPointerException!

Romain Linsolas
źródło
6

Zwarcie oznacza tutaj, że drugi warunek nie będzie oceniany.

Jeśli (A && B) spowoduje zwarcie, jeśli A jest fałszywe.

Jeśli (A && B) nie spowoduje zwarcia, jeśli A jest Prawdą.

Jeśli (A || B) spowoduje zwarcie, jeśli A jest Prawdą.

Jeśli (A || B) nie spowoduje zwarcia, jeśli A jest fałszywe.

user3211238
źródło
4

Nie, nie będzie, Java zakończy spięcie i przestanie oceniać, gdy zna wynik.

otchłań
źródło
4

Tak, ocena zwarcia dla wyrażeń boolowskich jest domyślnym zachowaniem w całej rodzinie C-podobnej.

Ciekawostką jest to, że Java wykorzystuje również operandy logiczne &i |jako (są one przeciążone, przy inttypach są oczekiwanymi operacjami bitowymi) do oceny wszystkich terminów w wyrażeniu, co jest również przydatne, gdy potrzebne są efekty uboczne.

fortran
źródło
Warto o tym pamiętać: np. Biorąc pod uwagę metodę changeData (dane), która zwraca wartość logiczną, to: if (a.changeData (dane) || b.changeData (dane)) {doSomething (); } nie wykonuje changeData na b, jeśli a.changeData () zwraca prawdę, ale jeśli (a.changeData (dane) | b.changeData (dane)) {doSomething ()} wykonuje changeData () na a i b, nawet jeśli ten wywołany na zwrócił true.
Sampisa
0

To wraca do podstawowej różnicy między & i &&, | i ||

Swoją drogą wykonujesz te same zadania wielokrotnie. Nie jestem pewien, czy problemem jest wydajność. Możesz usunąć część duplikatów.

Z z2 = partialHits.get(req_nr).get(z); // assuming a value cannout be null.
Z z3 = tmpmap.get(z); // assuming z3 cannot be null.
if(z2 == null || z2 < z3){   
    partialHits.get(z).put(z, z3);   
} 
Peter Lawrey
źródło