&& (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));  
}

где partialHitsхэш-карта
Что будет, если первое утверждение верно? Будет ли Java проверять второе утверждение? Потому что для того, чтобы первый оператор был истинным, HashMap не должен содержать заданный ключ, поэтому, если второй оператор проверен, я получу NullPointerException.
Итак, простыми словами, если у нас есть следующий код

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

будет ли Java проверять b, aявляется ли false в первом случае, и если atrue во втором случае?

Ответы:


202

Нет, это не будет оценено. И это очень полезно. Например, если вам нужно проверить, является ли строка пустой или пустой, вы можете написать:

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

или наоборот

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

Если бы у нас не было «коротких замыканий» в Java, мы получили бы много исключений NullPointerException в приведенных выше строках кода.


Существуют ли побитовые сравнения, чтобы вы могли оценить оба выражения? т.е. если (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 <= и наоборот
egallardo

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), но оценивает его правый операнд, только если значение его левого операнда равно true. [...] Во время выполнения выражение левого операнда вычисляется первым [...], если полученное значение равно false, значение условного выражения and равно false, а выражение правого операнда не оценивается , Если значение левого операнда равно true, тогда вычисляется правое выражение [...], результирующее значение становится значением выражения условного оператора и. Таким образом, && вычисляет тот же результат, что и & для булевых операндов. Он отличается только тем, что выражение правого операнда вычисляется условно, а не всегда.

И аналогично, в разделе 15:24, Оператор условного или (||) , говорится:

|| Оператор как | (§15.22.2), но оценивает его правый операнд, только если значение его левого операнда равно false. [...] Во время выполнения выражение левого операнда вычисляется первым; [...] если полученное значение равно true, значение условного выражения или true равно true, а выражение правого операнда не оценивается. Если значение левого операнда равно false, вычисляется правое выражение; [...] результирующее значение становится значением условного или выражения. Таким образом, || вычисляет тот же результат, что и | на булевых или булевых операндах. Он отличается только тем, что выражение правого операнда вычисляется условно, а не всегда.

Возможно, немного повторяющееся, но лучшее подтверждение того, как именно они работают. Аналогично, условный оператор (? :) вычисляет только соответствующую «половину» (левую половину, если значение равно true, правую половину, если она равна false), что позволяет использовать выражения типа:

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

без исключения NullPointerException.


6

Нет, если a равно true (в orтесте), b не будет проверяться, так как результат теста всегда будет true, независимо от значения выражения 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 (data) | b.changeData (data)) {doSomething ()} выполняет changeData () для a и b, даже если тот вызвал возвращенное значение true.
Самписа

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.