短路与非短路运营商
Posted
技术标签:
【中文标题】短路与非短路运营商【英文标题】:Short circuit vs non short circuit operators 【发布时间】:2013-08-23 08:19:51 【问题描述】:我理解下面的区别(至少对于 Java):
if( true || false ) // short-circuiting boolean operator
if( true | false ) // non-short-circuiting boolean operator
但我的问题是,在处理布尔表达式时是否有任何理由使用非短路运算符?练习?
【问题讨论】:
你不能不小心将|
用作位运算符;两个操作数都必须是布尔值或整数。在条件表达式中,您只能使用布尔值。
@cHao 是的,正确的。无论如何,在我看到您的评论之前将其删除,因为它与实际问题并没有那么相关。
Reason for the exsistance of non-short-circuit logical operators 的可能重复项
根据我的经验,98% 的非短路运算符是拼写错误,剩下的 2% 是糟糕的设计或危险代码。太容易错过了。想想开发人员在 5 年后维护您的代码。
【参考方案1】:
你不应该使用非短路运算符的唯一地方是当你想要执行第二条语句时,这在条件语句中通常不应该是这种情况
不,不短路没有性能,但短路绝对有好处
【讨论】:
【参考方案2】:如果你使用,
if(true || /*someStatementHere*/)
那么整个if
块将为真,因为第一个条件为真,所以它不必检查另一个
简单地说,如果第一个条件给出短路运算符的结果,则不会计算右侧操作数
【讨论】:
我了解其中的区别,我的问题更侧重于在仅处理布尔值时找到使用|
的好处。
唯一的好处是,短路操作员不会评估另一个条件
您要回答的问题与所问的问题有点相反。我们已经知道||
短路。问题是,是否有充分的理由更喜欢 |
而不是 ||
?【参考方案3】:
short-circuit
,这意味着如果没有必要,他们不会评估右侧。
例如,如果&&
左侧为假,则无需评估右侧。以其他方式||
如果左侧为真,则无需评估右侧。
non-short
总是评估双方。
那么显然short-circuit
运算符有一个好处。
还有非短的好处,可以在这里找到答案。Are there good uses for non-short-circuiting logical (boolean) operators in Java/Scala?
也考虑这个例子
while (status) // status is boolean value
if(!a | result)// a and result are boolean value
result=getResult(); // result can change time to time
我们现在需要检查双方。
【讨论】:
我理解其中的区别,我的问题更侧重于寻找在仅处理布尔值时使用|
的好处。
@kin3tik 我在回答中添加了更多信息【参考方案4】:
第三条语句
if( 10 | 11 ) // bitwise operator
会产生编译错误。 if 语句中只能放入布尔值。
如果您使用 Eclipse 之类的 IDE,它将自动显示第二个表达式,即 || 之后作为短路布尔运算符的死代码。
【讨论】:
你是对的。我将从问题中删除该行,因为它并不真正相关。【参考方案5】:您可能想要使用非短路运算符的一个原因是您是否以某种方式依赖于函数的副作用。例如。
boolean isBig(String text)
System.out.println(text);
return text.length() > 10;
...
if( isBig(string1) || isBig(string2) )
...
如果你不关心println
是否被执行,那么你应该使用上面的短路操作。但是,如果您希望始终打印两个字符串(因此取决于副作用),那么您需要使用非短路运算符。
实际上,您几乎总是希望使用短路运算符。依赖表达式中的副作用通常是不好的编程习惯。
一个例外是在非常低级别或对性能敏感的代码中。短路运算符可能会稍微慢一些,因为它们会导致程序执行中的分支。此外,使用按位运算符可以将 32 或 64 个并行布尔运算作为单个整数运算进行,速度非常快。
【讨论】:
啊,这是一个我没有考虑过的好点。但是依赖这样的效果通常不会被认为是一种不好的做法吗? 是的,在我看到您的评论之前,我在答案中添加了这一点!这确实是不好的做法。 通过添加的编辑,这似乎是我问题的最佳答案(涵盖 rhs 副作用和偶尔的性能优势)。【参考方案6】:对于简单的布尔表达式,|
有时比||
快。有时您希望对表达式进行统一评估(例如在索引值中有 ++
时。
您不能意外地“混合”布尔值|
和按位|
,因为您不能混合布尔值和整数操作数。但是您可以(当然)在您打算使用||
的地方意外使用|
。谢天谢地,它不是 C,您可能会不小心使用 =
而不是 ==
。
【讨论】:
boolean b = false; if (b = true) /*do stuff*/
实际上是允许的,并且可以做一些事情。这有点与你的最后一句话混淆。 :)
@cHao - 是的,但你不能说if (intVar1 = intVar2)
并让它通过Java 编译器。但是,C 会很乐意接受它。 (而您的示例是人们应该避免使用 boolValue == true (or false)
之类的表达式而不是简单地使用 boolValue
或 !boolValue
的原因之一。)【参考方案7】:
我的问题更侧重于寻找使用 | 的好处仅处理布尔值时
考虑以下情况
while ( !credentialvalid() | (loginAttempts++ < MAX) )
// tell something to user.
在这种情况下需要|
,因为我也必须增加尝试次数:)
【讨论】:
这是一个(非常)迟到的评论,但我建议不要使用 |以这种方式。这不直观,理想情况下,如果必须始终执行 loginAttempts 增量,则将其移出条件。【参考方案8】:如果您的代码对性能足够敏感并且操作足够便宜,则使用非短路可以更快。这是因为使用||
涉及执行分支,并且分支预测未命中可能非常昂贵。当|
执行计算和检查变量时,可以更快地避免分支预测失误。
注意:这是一个微优化,除非被多次调用,否则您很少会看到差异。
【讨论】:
理论上,JIT 可以将任一情况优化到 RHS 没有副作用的另一种情况,因此在理想情况下,性能不会成为考虑因素(您始终可以使用 @ 987654323@ 用于无副作用比较)。例如,Guava 的LongMath
类确实 使用此优化进行溢出检查:(a ^ b) < 0 | (a ^ result) >= 0
。我还没有检查 Hotspot 是否足够聪明,可以实际上为这两种情况编译相同的代码。这就像三元运算符可能暗示无分支代码与 if/else 相比,但好的编译器在两种情况下都会生成相同的代码。
@BeeOnRope 在许多情况下,JIT 足够聪明,可以使用 Conditional Move 汇编指令。例如这意味着 max/min a > b ? a : b
表达式不需要分支。
确实 - 我的评论更多是关于优化有时如何使性能建议过时。特别是,在没有优化的 gcc 中,if/else
总是生成分支,而三元运算符生成CMOV
(在可能的情况下,例如,选择是原语)。鉴于此,您可能会开始建议在分支不可预测时使用 ?:
,因为编译器对它们的处理方式不同。但是,在您打开优化的那一刻,差异就消失了:两者都被编译为无分支代码。我怀疑||
和|
会出现类似的等价关系。【参考方案9】:
我不知道为什么所有人都说按位只能对布尔操作数进行操作。 我告诉你按位运算符适用于操作数的类型,并根据操作数返回相同类型的值。只是为了理解,将按位运算符视为可以添加 2 个数字的数学“+”运算符。按位相同。
if(10 | 11) 不会给出编译错误。如果结果为 0,则为假,否则为真。
BITWISE OPERATORS 不会短路,因为按位总是需要 2 个操作数才能执行。小心按位使用 OPERANDS 而不是 Boolean_CONDITIONs
语法:
for bitwise : (operand_1) bitwise_operator (operand_2)
for example: ( 2 ) | ( 3 )
for LOGICAL : (Boolean_condition_1) LOGICAL_operator (Boolean_condition_2)
for example: ( true ) || ( false )
【讨论】:
以上是关于短路与非短路运营商的主要内容,如果未能解决你的问题,请参考以下文章