&& (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.