Java逻辑运算符短路

131

哪个集合发生了短路,复杂的条件表达式短路是什么意思?

public static void main(String[] args) {
  int x, y, z;

  x = 10;
  y = 20;
  z = 30;

  // T T
  // T F
  // F T
  // F F

  //SET A
  boolean a = (x < z) && (x == x);
  boolean b = (x < z) && (x == z);
  boolean c = (x == z) && (x < z);
  boolean d = (x == z) && (x > z);
  //SET B    
  boolean aa = (x < z) & (x == x);
  boolean bb = (x < z) & (x == z);
  boolean cc = (x == z) & (x < z);
  boolean dd = (x == z) & (x > z);

}

4
请看这个问题:https://dev59.com/S2w05IYBdhLWcg3wqzms - Eng.Fouad
集合B不使用逻辑运算符。它们使用按位运算符比较操作数的每个位。 - NomadMaker
10个回答

295

&&|| 运算符具有 "短路" 特性,也就是说如果右侧表达式的值不必要,它们不会对右侧表达式进行求值。

当作为逻辑运算符使用时,&| 运算符始终会对两侧表达式进行求值。

每个运算符只有一种情况会发生短路,分别是:

  • false && ... - 无需知道右侧表达式的值,因为结果只能为 false
  • true || ... - 无需知道右侧表达式的值,因为结果只能为 true

让我们在一个简单的例子中比较它们的行为:

public boolean longerThan(String input, int length) {
    return input != null && input.length() > length;
}

public boolean longerThan(String input, int length) {
    return input != null & input.length() > length;
}

第二个版本使用非短路的操作符&,如果inputnull,将会抛出一个NullPointerException异常。但是第一个版本则不会抛出异常而是返回false


13
我想要进一步解释一下这个答案。"&=" 运算符是 "x = x & expression" 的简写,因此它不会短路运算。 "|=" 运算符也是同样的情况。 - Stormcloud
13
有一件事我想要强调,| 和 & 是二进制运算符,而 && 和 || 是条件(逻辑)运算符。| 和 & 不仅适用于布尔值,而且还适用于其他类型的数据,而 && 和 || 只适用于布尔值。 - A myth
2
不仅不会评估右侧的表达式,而且代码也不会执行,因此没有任何需要评估的内容。如果存在副作用,这是理解的关键点。 - mckenzm
@Kronen 执行可能会导致比计算更多的结果,并产生副作用,例如异常或延迟,本例不涉及支付。 - mckenzm
@CollinAlpert 中间的 true 不会被评估,因为 && 短路了。 - Donnie
显示剩余5条评论

10

SET A使用短路布尔运算符。

在布尔运算符的上下文中,“短路”意味着对于一组布尔值b1,b2,...,bn,短路版本将在第一个为真(||)或假(&&)时停止评估。

例如:

// 2 == 2 will never get evaluated because it is already clear from evaluating
// 1 != 1 that the result will be false.
(1 != 1) && (2 == 2)

// 2 != 2 will never get evaluated because it is already clear from evaluating
// 1 == 1 that the result will be true.
(1 == 1) || (2 != 2)

请指定这适用于 &&,而 || 的工作方式不同,并且将在第一个返回 true 的操作数上停止评估 ;) - fge
2
实际上,为了真正完整,所有的 &&||&| 都是从左到右进行评估的。对于一组布尔值 b1、b2、...、bn,短路版本将在第一个布尔值为 true (||) 或 false (&&) 时停止评估。嗯,原则就在那里 ;) - fge
@fge:是的,你当然是正确的。你的定义比我的更精确。我已经用你评论中的句子更新了我的答案。希望你不介意。 - afrischke
不用担心,如果知识不被分享,它就没有价值。 - fge

4
简而言之,“短路”意味着一旦你知道答案不会再改变,就停止评估。例如,如果你正在评估一系列逻辑“AND”,并且在该链的中间发现了一个“FALSE”,则无论该链中其余表达式的值如何,你都知道结果将是false。同样适用于一系列的“OR”:一旦你发现了一个“TRUE”,你就立刻知道答案,因此可以跳过评估其余表达式。
通过使用“&&”而不是“&”以及“||”而不是“|”,你告诉Java你想要进行“短路”。你在帖子中提到的第一组是短路的。
请注意,这不仅仅是为了节省一些CPU周期:在这种表达式中。
if (mystring != null && mystring.indexOf('+') > 0) {
    ...
}

短路指的是正确运行和崩溃之间的差异(在mystring为空的情况下)。

4

短路运算是指如果第一个操作符决定了最终结果,第二个操作符将不会被检查。

例如,表达式为:True || False

在||的情况下,我们只需要其中一个为True。因此,如果左侧为True,则没有必要检查右侧,因此右侧将不会被检查。

同样,False && True

在&&的情况下,我们需要两边都为True。因此,如果左侧为False,则没有必要检查右侧,答案必须为False。因此右侧将不会被检查。


4
boolean a = (x < z) && (x == x);

这种方式会发生短路,也就是说,如果(x < z)的结果为false,则后者不会被计算,a将会是false;否则&&将会计算(x == x)&是一个按位运算符,也是一个布尔AND运算符,它不会发生短路。
你可以通过以下方式进行测试(查看每种情况下该方法被调用的次数)。
public static boolean getFalse() {
    System.out.println("Method");
    return false;
}

public static void main(String[] args) {
    if(getFalse() && getFalse()) { }        
    System.out.println("=============================");        
    if(getFalse() & getFalse()) { }
}

您的回答表明 & 只是一个位运算符,但这并不正确。它也是一个布尔“或”运算符。 - Bohemian
@Bohemian:感谢你提醒,true & false的结果是false。你能否解释一下"布尔"或"运算符"?可能我没有理解你的意思。 - Bhesh Gurung
抱歉 - 我的意思是布尔运算中的 AND,而不是 OR!即 true & false 是有效的语法。已移除 -1 :) - Bohemian

3
&&&操作符之间有一些不同。同样的差异也适用于|||。最重要的是,&&是一个逻辑运算符,仅适用于布尔操作数,而&是一个按位运算符,适用于整数类型以及布尔值。
使用逻辑运算,可以进行短路处理,因为在某些情况下(例如&&的第一个操作数为false||的第一个操作数为true),您不需要计算表达式的其余部分。这对于诸如在访问字段或方法之前检查null,以及在除以可能为零的数字之前检查潜在的零等操作非常有用。对于复杂表达式,每个表达式部分都以相同的方式递归地计算。例如,在以下情况下:
(7 == 8) || ((1 == 3) && (4 == 4))
只有突出显示的部分将被计算。要计算||,首先检查是否7 == 8true。如果是,那么右侧将被完全跳过。右侧仅检查1 == 3是否为false。由于它是,无需检查4 == 4,整个表达式计算为false。如果左侧为true,例如7 == 7而不是7 == 8,则整个右侧将被跳过,因为整个||表达式都将是true
使用按位操作,需要评估所有操作数,因为您实际上只是组合位。在Java中,布尔类型实际上是一个一位的整数(无论内部如何工作),并且在那种特殊情况下可以对按位运算符进行短路处理只是巧合。您无法短路普通整数&|操作的原因是某些位可能在任一操作数中处于打开或关闭状态。例如,1 & 2产生零,但是如果没有计算两个操作数,您就无法知道这一点。

2

逻辑或:- 如果至少有一个操作数为真,则返回 true。在应用 OR 运算符之前,将评估两个操作数。

短路或:- 如果左侧操作数返回 true,则返回 true,而无需评估右侧操作数。


2

Java提供了两个有趣的布尔运算符,这些运算符在大多数其他计算机语言中都找不到。这些AND和OR的次要版本被称为短路逻辑运算符。如您从上表所见,当A为真时,OR运算符结果为true,无论B是什么。

同样,当A为假时,AND运算符的结果为false,无论B是什么。如果您使用||&&形式而不是这些运算符的|&形式,则Java将不会单独评估右操作数。当右操作数取决于左操作数为真或假以便正常运行时,这非常有用。

例如,下面的代码片段展示了如何利用短路逻辑运算来确保在进行求值之前除法操作是有效的:

if ( denom != 0 && num / denom >10)

由于使用了AND的短路形式(&&),因此没有导致除以零而引起运行时异常的风险。如果使用单个&的AND版本编写此行代码,则需要评估双方,当denom为零时会导致运行时异常。
在涉及布尔逻辑的情况下,使用AND和OR的短路形式是标准实践,将单字符版本专用于位运算。但是,也有例外。例如,请考虑以下语句:
 if ( c==1 & e++ < 100 ) d = 100;

在这里,使用单个&可以确保增量操作将应用于e,无论c是否等于1。

1
if(demon!=0&& num/demon>10)

由于使用了AND(&&)的短路形式,因此当demon为零时不会导致运行时异常。
参考:Herbert Schildt的Java 2第五版。

1

来自docs.oracle的文档

由于逻辑表达式从左到右进行计算,因此使用以下规则测试可能的“短路”评估:

false && anything会被短路评估为false。

true || anything会被短路评估为true。

逻辑规则保证这些评估始终是正确的。请注意,上述表达式的任何部分都不会被评估,因此不会产生任何副作用。

https://docs.oracle.com/cd/E57185_01/HIRUG/ch31s05s01.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接