在 IF 语句中 & & (AND)和 | | (OR)

我有以下密码:

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。因为为了使第一个语句为 true,HashMap 不应该包含给定的键,所以如果选中第二个语句,我将得到 NullPointerException
简单来说,如果我们有以下代码

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

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

1971588 次浏览

这种行为被称为 短路求值,是包括 Java 在内的许多语言的一个特性。

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

做一个简单的测试:

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

没有抛出一个 NullPointerException

不,它不会,Java 将短路和停止评估一旦它知道的结果。

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

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

或者反过来

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

如果我们在 Java 中没有短路,我们会在上面的代码行中收到大量的 NullPointerException。

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

第15章23节,条件运算符(& &) 说:

& & 操作符类似于 & (15.22.2) ,但是只有当其左操作数的值为真时才计算其右操作数。[ ... ]在运行时,如果结果值为 false,则首先计算左操作数表达式[ ... ] ,条件-和表达式的值为 false,并且不计算右操作数表达式。如果左边的操作数的值为 true,那么右边的表达式将被求值[ ... ] ,得到的值将成为条件表达式和表达式的值。因此,& & 计算的结果与 & 对布尔操作数的结果相同。它的不同之处仅在于右边的操作数表达式是有条件计算的,而不是总是计算。

同样,第15节: 24,条件或操作符(| |)表示:

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

也许有点重复,但是最能确定它们是如何工作的。同样的条件运算符(?:)只计算适当的‘ half’(如果值为 true,则计算左半,如果值为 false,则计算右半) ,允许使用如下表达式:

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

没有 NullPointerException。

是的,布尔表达式的短路求值是所有类 C 语言家族中的默认行为。

一个有趣的事实是,Java 还使用 &|作为逻辑操作数(它们被重载,使用 int类型它们是预期的按位操作)来计算表达式中的所有术语,这在需要副作用时也很有用。

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

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

在检查参数的值之前,单个参数将检查每个参数,而不管这些参数的值是什么。 双参数将首先检查左参数及其值,如果 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 无法优化,因为它取决于这两个参数。

这可以追溯到 & 和 & & ,| 和 | | 之间的基本区别

顺便说一下,你执行相同的任务很多次。不确定效率是否是个问题。你可以删除一些副本。

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

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

如果(A & B)会导致短路,如果 A 是错误的。

如果(A & B)将导致 没有短路,如果 A 为真。

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

如果(A | | B)会导致 没有短路如果 A 是假的。