&& (AND) та || (АБО) у твердженнях IF


137

У мене є такий код:

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

де partialHitsHashMap.
Що буде, якщо перше твердження вірно? Чи перевірятиме Java все-таки друге твердження? Тому що для того, щоб перше твердження було правдивим, HashMap не повинен містити заданий ключ, тому, якщо перевіряється другий вислів, я отримаю NullPointerException.
Тож простими словами, якщо у нас є наступний код

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

Чи перевірить Java, bчи aнеправда в першому випадку, а якщо aв другому - істина?

Відповіді:


202

Ні, це не буде оцінено. І це дуже корисно. Наприклад, якщо вам потрібно перевірити, чи рядок не є нульовим чи порожнім, ви можете написати:

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

або, навпаки

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

Якби у нас не було "коротких замикань" на Java, ми отримали б багато NullPointerExceptions у вищевказаних рядках коду.


Чи існують побізні порівняння, щоб ви могли оцінити обидва вирази? тобто якщо (str! = null | str.isEmpty ())? (звичайно, це не практичний приклад, насправді це
дурно

5
Поки вирази не мають побічних ефектів, семантика короткого замикання логічно еквівалентна повної оцінки. Тобто, якщо A відповідає дійсності, ви знаєте, що A || B відповідає дійсності, не оцінюючи B. Єдиний раз, коли це зміниться, це якщо вираз має побічні ефекти. Що стосується інших операторів, ви можете використовувати *і +як логічні, andі or; ((A?1:0) * (B?1:0)) == 1, ((A?1:0) + (B?1:0)) > 0. Ви навіть можете зробити xor: ((A?1:0) + (B?1:0)) == 1.
outis

1
@Kezzer: це насправді побітове порівняння? Я думаю, що це boolean(логічний) оператор. Це відрізняється від bitwise(цілого) оператора, незважаючи на те, що він має той самий символ ...
user85421

4
Зручний трюк, коли потрібно переключитися між "&&" та "||" вирази - це заперечувати весь вираз таким чином, що: !(str != null && !str.isEmpty()) стає: (str !(!=) null !(&&) !(!)str.isEmpty()) а потім: (str == null || str.isEmpty()) тому що: !(!=) is == !(&&) is || !(!) eliminates itself іншими корисними запереченнями є: !(<) is >= !(>) is <= і
viceversa

68

У Java є 5 різних булевих операторів порівняння: &, &&, |, ||, ^

& і && є "та" операторами, | та || "або" операторів, ^ є "xor"

Одиничні перевірять кожен параметр, незалежно від значень, перед тим, як перевірити значення параметрів. Подвійні спочатку перевірять лівий параметр та його значення, а якщо true( ||) або false( &&) залишають другий недоторканим. Звук складний? Простий приклад повинен чітко пояснити:

Наведено для всіх прикладів:

 String aString = null;

І:

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

Обидва параметри перевіряються до того, як буде проведена оцінка, і для другого параметра буде передано NullPointerException.

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

Перший параметр перевіряється і він повертається false, тому другий параметр не перевірятиметься, оскільки результат все- falseтаки є .

Те саме для АБО:

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

Підвищить і NullPointerException.

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

Перший параметр перевіряється і він повертається true, тому другий параметр не перевірятиметься, оскільки результат все- trueтаки є .

Неможливо оптимізувати XOR, оскільки це залежить від обох параметрів.


3
"У Java є 4 різні оператори булевого порівняння: &, &&, |, ||" ... Ви забуваєте ^(xor).
aioobe

О, я не знав, що він перевіряє булеві булеві значення. Поки що його використовували лише для растрових масок.
Жорстко кодований


20

Усі відповіді тут чудові, але для того, щоб проілюструвати, звідки це походить, для таких питань добре перейти до джерела: Специфікація мови Java.

Розділ 15:23, Оператор "Умовно-І" (&&) , говорить:

Оператор && схожий на & (§ 15.22.2), але він оцінює його праворучний операнд, лише якщо значення його лівого операнда є істинним. [...] Під час виконання вираз лівого операнда спочатку оцінюється [...] якщо отримане значення хибне, значення умовного і виразу хибне, а вираз правого операнда не оцінюється . Якщо значення лівого операнда є істинним, то праворучне вираження оцінюється [...], отримане значення стає значенням умовно-вираження. Таким чином, && обчислює той же результат, що і на булевих операндах. Він відрізняється лише тим, що вираз правого операнда оцінюється умовно, а не завжди.

І так само, Розділ 15:24, Оператор «Умовно-Або» (||) говорить:

|| оператор схожий на | (§15.22.2), але оцінює його праворучний операнд лише у тому випадку, якщо значення його лівого операнда хибне. [...] Під час виконання спочатку оцінюється вираз лівого операнду; [...] якщо отримане значення є істинним, значення умовного або виразу є істинним, а вираз правого операнда не оцінюється. Якщо значення лівого операнда є хибним, то виражається правий вираз; [...] отримане значення стає значенням умовного або виразу. Таким чином, || обчислює той самий результат, що і | на булевих або булевих операндах. Він відрізняється лише тим, що вираз правого операнда оцінюється умовно, а не завжди.

Можливо, трохи повторюваний, але найкраще підтвердження того, як саме вони працюють. Аналогічно умовний оператор (? :) оцінює лише відповідну "половину" (ліва половина, якщо значення є істинною, права половина, якщо вона помилкова), дозволяючи використовувати вирази типу:

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

без NullPointerException.


6

Ні, якщо a є істинним (у orтесті), b не перевірятиметься, оскільки результат тесту завжди буде істинним, незалежно від значення виразу b.

Зробіть простий тест:

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

буде НЕ кидати NullPointerException!


6

Коротке замикання тут означає, що друга умова не буде оцінена.

Якщо (A&B) призведе до короткого замикання, якщо A є помилковим.

Якщо (A & B) не призведе до короткого замикання, якщо A - істина.

Якщо (A || B) призведе до короткого замикання, якщо A є правдою.

Якщо (A || B) не призведе до короткого замикання, якщо A є помилковим.


4

Ні, це не стане, Java замикається на короткому замиканні та припиняє оцінювати, як тільки дізнається результат.


4

Так, оцінка короткого замикання для булевих виразів є поведінкою за замовчуванням у всій C-подібній родині.

Цікавим фактом є те, що Java також використовує логічні операнди &і |як (вони перевантажені; intтипи - це очікувані побітні операції) для оцінки всіх термінів у виразі, що також корисно, коли потрібні побічні ефекти.


Це цікаво пам’ятати: наприклад, заданий метод changeData (data), який повертає булеве значення, тоді: if (a.changeData (data) || b.changeData (data)) {doSomething (); } не виконує changeData на b, якщо a.changeData () повертає значення true, але якщо (a.changeData (дані) | b.changeData (дані)) {doSomething ()} виконує changeData () на а і b, навіть якщо виклик повернутої істини.
Самписа

0

Це повертається до основної різниці між & і &&, | та ||

До речі, ви виконуєте одні і ті ж завдання багато разів. Не впевнений, чи ефективність - це проблема. Ви можете видалити частину дублювання.

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);   
} 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.