2009-11-25 3 views
101

Moje pytanie może być bardzo proste, ale nadal myślę, że warto o to zapytać. Mam następujący kod:&& (AND) i || (OR) w instrukcjach IF

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 partialHits jest HashMap. Co się stanie, jeśli pierwsze zdanie będzie prawdziwe? Czy Java nadal sprawdzi drugie zdanie? Ponieważ aby pierwsza instrukcja była prawdziwa, HashMap nie powinna zawierać podanego klucza, więc jeśli zaznaczona jest druga instrukcja, otrzymam NullPointerException.
Tak więc w prostych słowach, jeśli mamy następujący kod

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

by sprawdzić Java b jeśli a jest fałszywe w pierwszym przypadku i jeśli a prawda w drugim przypadku?

Odpowiedz

158

Nie będzie oceniany. Jest to bardzo przydatne. Jak trzeba sprawdzić, jeśli ciąg nie jest null lub pusty, można napisać:

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

lub, na odwrót

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

Jeśli nie będziemy mieli „zwarć” w Java, otrzymamy wiele NullPointerExceptions w powyższych liniach kodu.

+0

Czy istnieją porównania bitowe, aby móc ocenić oba wyrażenia? tj. if (str! = null | str.isEmpty())? (Oczywiście nie jest to praktyczny przykład, w gruncie rzeczy jest głupie, ale masz pomysł) – Kezzer

+5

Dopóki wyrażenia nie mają skutków ubocznych, semantyka zwarcia jest logicznie równoważna z całkowitą oceną. Oznacza to, że jeśli A jest prawdziwe, to wiesz, że A || B jest prawdziwe bez konieczności oceny B. Jedynym momentem, w którym robi różnicę, jest to, czy wyrażenie ma skutki uboczne. Podobnie jak w przypadku innych operatorów, możesz użyć '*' i '+' jako logicznych 'i' oraz' lub'; '((A? 1: 0) * (B? 1: 0)) == 1',' ((A? 1: 0) + (B? 1: 0))> 0'. Możesz nawet zrobić 'xor':' ((A? 1: 0) + (B? 1: 0)) == 1'. – outis

+1

@Kezzer: czy to naprawdę porównanie bitowe? Myślę, że jest to operator 'boolean' (logiczny). Jest inny niż operator 'bitwise' (integer), pomimo tego samego symbolu ... –

27

Nie będzie to zaznaczone. To zachowanie nazywa się short-circuit evaluation i jest funkcją w wielu językach, w tym w Javie.

+2

@Azimuth: Nie ma za co. Jeśli czegoś nie wiesz, najlepszym rozwiązaniem jest zapytanie. –

5

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

Zrób prosty test:

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

będzie nie rzucać NullPointerException!

4

Nie, Java nie spowoduje zwarcia i przestanie oceniać, gdy zna wynik.

18

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

Section 15:23, Conditional-And operator (&&), mówi:

& & operator jest jak & (§15.22.2), ale ocenia jego prawy operand tylko wtedy, gdy wartość jego lewej argument jest prawdziwy. [...] W czasie wykonywania lewe wyrażenie operandu jest oceniane najpierw [...], jeśli wynikowa wartość jest fałszywa, wartość warunkowego - i wyrażenie jest fałszywe, a prawe wyrażenie operandu nie jest oceniane . Jeśli wartość lewego argumentu jest prawdą, to wyrażenie prawostronne jest oceniane [...], a wynikowa wartość staje się wartością warunkowego-i wyrażenia. Tak więc, & & oblicza taki sam wynik, jak & na operandach logicznych. Różni się tylko tym, że prawe wyrażenie operandu jest oceniane warunkowo, a nie zawsze.

I podobnie, Section 15:24, Conditional-Or operator (||), mówi:

The || operator jest jak | (§ 15.22.2), ale ocenia swój prawy operand tylko wtedy, gdy wartość jego lewego argumentu jest fałszywa. [...] W czasie wykonywania lewe wyrażenie operandu jest obliczane jako pierwsze; [...] jeśli wynikowa wartość jest prawdziwa, wartość warunkowego wyrażenia jest prawdą, a prawe wyrażenie operandu nie jest oceniane. Jeśli wartość lewego argumentu jest fałszywa, to wyrażenie prawej ręki jest oceniane; [...] wynikowa wartość staje się wartością warunkowego-lub wyrażenia. Tak więc, || oblicza ten sam wynik co | na operatory logiczne lub boolowskie. Różni się tylko tym, że prawe wyrażenie operandu jest oceniane warunkowo, a nie zawsze.

Trochę monotonne, ale najlepsze potwierdzenie tego, jak działają. ? Podobnie operator warunkowy (:) ocenia odpowiedni „połowę” (lewa połowa tylko wtedy, gdy wartość jest prawdą, prawda pół jeśli jest fałszywe), co pozwala na korzystanie z wyrażeń takich jak:

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

bez NullPointerException.

4

Tak, ocena zwarcia dla wyrażeń logicznych jest domyślnym zachowaniem we wszystkich rodzinach podobnych do C.

Ciekawostką jest fakt, że Java wykorzystuje również & i | jako argumentów logicznych (są przeciążone z int typów są one oczekiwanego operacje bitowe), aby ocenić wszystkie warunki w ekspresji, która jest również przydatna, gdy trzeba skutki uboczne.

+0

Warto o tym pamiętać: np. podano metodę changeData (dane), która zwraca wartość boolowską, a następnie: , jeśli (a.changeData (dane) || b.changeData (dane)) {doSomething(); } nie wykonuje changeData na b jeśli a.changeData() zwraca true, ale if (a.changeData (dane) | b.changeData (dane)) {doSomething()} wykonuje changeData() na zarówno a, jak i b, nawet jeśli jeden wywołany na zwróconej wartości true. – Sampisa

51

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

& i & & są "i" operatorów | i || "lub" operatory,^to "xor"

Pojedyncze sprawdzają każdy parametr, niezależnie od wartości, przed sprawdzeniem wartości parametrów. Podwójne sprawdzi najpierw lewy parametr i jego wartość, a jeśli true (||) lub false (&&) pozostawi drugi nietknięty. Kompilacja dźwięku? Prostym przykładem powinna jasno:

Biorąc pod uwagę dla wszystkich przykładów:

String aString = null; 

I:

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

Oba parametry są sprawdzane przed ewaluacja jest zrobione i NullPointerException zostanie wyrzucony dla drugiego parametru.

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

Pierwszy parametr jest sprawdzany i zwraca false, więc nie będą sprawdzane drugi paramter, ponieważ wynik jest false tak.

To samo dla OR:

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

podniesie NullPointerException, too.

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

Pierwszy parametr jest sprawdzany i zwraca true, więc nie będą sprawdzane drugi paramter, ponieważ wynik jest true tak.

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

+2

"Java ma 4 różne operatory porównania boolowskiego: &, &&, |, ||" ... Zapominasz '^' (xor). – aioobe

+0

Och, nie wiedziałem, że sprawdza również wartości boolowskie boolowskie. Używał go do tej pory tylko dla bitmask. – Hardcoded

0

To sięga podstawowej różnicy między & a & &, | i ||

BTW wykonuje się te same czynności wiele razy. Nie jestem pewien, czy wydajność jest problemem. Możesz usunąć część duplikacji.

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); 
} 
4

Zwarcie oznacza tutaj, że drugi warunek nie zostanie oceniony.

Jeśli (A & & B) spowoduje zwarcie, jeśli A jest Fałsz.

Jeżeli (A & & B) będzie nie wynik w zwarciu, jeśli A jest prawdziwe.

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

Jeśli (A || B) będzie , nie spowoduje zwarcie, jeśli A jest Fałsz.