ChatGPT解决这个技术问题 Extra ChatGPT

&& (AND) 和 || (OR) 在 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));  
}

其中 partialHits 是一个 HashMap。
如果第一个语句为真会发生什么? Java 还会检查第二个语句吗? 因为要使第一个语句为真,HashMap 不应该包含给定的键,所以如果第二条语句被选中,我会得到 NullPointerException
简单来说,如果我们有以下代码

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

如果在第一种情况下 a 为假,而在第二种情况下 a 为真,Java 会检查 b 吗?


Z
Zubin Mukerjee

不,它不会被评估。这非常有用。例如,如果您需要测试一个 String 是否为 null 或为空,您可以编写:

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

或者,反过来

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

如果我们在 Java 中没有“短路”,我们会在上面的代码行中收到很多 NullPointerExceptions。


是否存在按位比较,以便您可以评估这两个表达式?即 if(str != null | str.isEmpty()) ? (当然这不是一个实际的例子,实际上它很愚蠢,但你明白了)
只要表达式没有副作用,短路语义在逻辑上就等同于完整的求值。也就是说,如果 A 为真,您无需评估 B 即可知道 A||B 为真。唯一会产生影响的情况是表达式是否有副作用。至于其他运算符,您可以使用 *+ 作为逻辑 andor((A?1:0) * (B?1:0)) == 1((A?1:0) + (B?1:0)) > 0。你甚至可以做xor((A?1:0) + (B?1:0)) == 1
@Kezzer:这真的是按位比较吗?我认为它是一个 boolean (逻辑)运算符。它与 bitwise (整数)运算符不同,尽管具有相同的符号......
当你想在'&&'之间切换时的一个方便的技巧和'||'表达式是否定整个表达式,这样: !(str != null && !str.isEmpty()) 变为: (str !(!=) null !(&&) !(!)str.isEmpty()) 然后: (str == null || str.isEmpty()) 因为: !(!=) is == !(&&) is || !(!) eliminates itself 其他有用的否定是: !(<) is >= !(>) is <= 和反之亦然
H
Hardcoded

Java 有 5 种不同的布尔比较运算符:&、&&、|、||、^

& 和 && 是“和”运算符,|和 || “或”运算符,^ 是“异或”

在检查参数值之前,单个参数将检查每个参数,而不管值如何。双重的将首先检查左边的参数及其值,如果 true (||) 或 false (&&) 保持第二个不变。听起来很复杂?一个简单的例子应该清楚:

给出所有示例:

 String aString = null;

和:

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

在评估完成之前检查这两个参数,并且将为第二个参数抛出 NullPointerException。

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

检查第一个参数并返回 false,因此不会检查第二个参数,因为无论如何结果都是 false

与 OR 相同:

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

也会引发 NullPointerException。

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

检查第一个参数并返回 true,因此不会检查第二个参数,因为无论如何结果都是 true

XOR 无法优化,因为它取决于两个参数。


“Java 有 4 种不同的布尔比较运算符:&、&&、|、||”...您忘记了 ^(xor)。
哦,我不知道它也检查布尔值。到目前为止,仅将其用于位掩码。
h
heijp06

不,它不会被检查。这种行为称为 short-circuit evaluation,是包括 Java 在内的许多语言的一项功能。


C
Cowan

这里的所有答案都很好,但是为了说明这是从哪里来的,对于这样的问题,最好去源头:Java 语言规范。

Section 15:23, Conditional-And operator (&&),说:

&& 运算符类似于 &(第 15.22.2 节),但仅当其左侧操作数的值为真时才计算其右侧操作数。 [...] 在运行时,首先计算左侧操作数表达式 [...] 如果结果值为 false,则条件与表达式的值为 false 并且不计算右侧操作数表达式.如果左侧操作数的值为真,则计算右侧表达式 [...] 结果值成为条件与表达式的值。因此, && 在布尔操作数上计算与 & 相同的结果。它的不同之处仅在于右手操作数表达式是有条件地计算而不是总是计算。

同样,Section 15:24, Conditional-Or operator (||) 说:

||运算符就像 | (§15.22.2),但仅当其左侧操作数的值为假时才评估其右侧操作数。 [...] 在运行时,首先计算左侧操作数表达式; [...] 如果结果值为真,则条件或表达式的值为真,并且不计算右侧操作数表达式。如果左侧操作数的值为 false,则计算右侧表达式; [...] 结果值成为条件或表达式的值。因此,||计算与 | 相同的结果在布尔或布尔操作数上。它的不同之处仅在于右手操作数表达式是有条件地计算而不是总是计算。

可能有点重复,但最好确认它们是如何工作的。类似地,条件运算符(?

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

没有 NullPointerException。


G
Gaunt

这里的短路意味着不会评估第二个条件。

If ( A && B ) 如果 A 为 False 将导致短路。

If ( A && B ) 如果 A 为真,则不会导致短路。

If ( A || B ) 如果 A 为真,将导致短路。

If ( A || B ) 如果 A 为 False,则不会导致短路。


R
Romain Linsolas

不,如果 a 为真(在 or 测试中),则不会测试 b,因为无论 b 表达式的值是什么,测试的结果都将始终为真。

做一个简单的测试:

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

不会抛出 NullPointerException


a
abyx

不,它不会,一旦知道结果,Java 就会短路并停止评估。


f
fortran

是的,布尔表达式的短路求值是所有类 C 系列的默认行为。

一个有趣的事实是,Java 还使用 &| 作为逻辑操作数(它们是重载的,对于 int 类型,它们是预期的按位运算)来评估表达式中的所有项,这在以下情况下也很有用你需要副作用。


记住这一点很有趣:例如,给定一个返回布尔值的方法 changeData(data),然后: if (a.changeData(data) || b.changeData(data)) { doSomething();如果 a.changeData() 返回 true,则不会在 b 上执行 changeData,但如果 (a.changeData(data) | b.changeData(data)) { doSomething() } 在 a 和 b 上执行 changeData(),甚至如果在返回的 true 上调用。
P
Peter Lawrey

这又回到了 & 和 && 之间的基本区别,|和 ||

顺便说一句,您多次执行相同的任务。不确定效率是否是一个问题。您可以删除一些重复项。

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