使用异或运算符进行布尔检查是否是良好的实践?

161

在布尔检查的情况下,我个人喜欢使用异或(exclusive or)运算符^,因为它简洁明了。因此,我更喜欢这样写:

if (boolean1 ^ boolean2)
{
  //do it
}

if((boolean1 && !boolean2) || (boolean2 && !boolean1))
{
  //do it
}

但是我经常从其他有经验的Java开发人员(不仅仅是新手)那里得到困惑的表情,有时还会听到关于它只能用于位运算的评论。

我很好奇关于使用^操作符的最佳实践。

12个回答

313

你可以简单地使用!=代替。


37
“!=”有什么问题?对我来说,bool1 ^ bool2 ^ bool3bool1 != bool2 != bool3 更具逻辑意义。 - BlueRaja - Danny Pflughoeft
4
我脑袋疼。那么can !=会导致错误的结果吗? - deprecated
28
@vemv,!=对于boolean类型的值可以产生正确的结果(但对于Boolean类型的值则需要注意)。然而,它并不总是很清晰易读。例如,(some != null) != (other != null)这个表达式并不容易理解。你可以将其分解为明确的布尔值,或将!=操作提取到一个单独的方法中。 - ivant
23
为什么会这样呢:a ^ b 的意思是“a或b但不能同时出现”,而 a != b 的意思是“a不等于b”。这就是@RobertGrant所说的内容。如果能够理解异或运算的话(在计算领域非常有用),那么大多数人就更容易理解第一个表达式了。 - Harold R. Eason
2
@HaroldR.Eason 这里有一个重要的细节需要注意:a != b => "a 不等同于 b" - Mario Reutter
显示剩余4条评论

27

我认为你已经回答了自己的问题-如果人们向你投来奇怪的眼光,最好选择更明确的选项。

如果你需要进行评论,那么最好用更详细的版本替换它,不要让人们首先提出问题。


6
如果你在真正的应用程序代码中认真地写下(boolean1 && !boolean2) || (boolean2 && !boolean1),我可以断言,我会对你投以奇怪的眼神。 - Holger

18

我发现我经常会有类似的对话。一方面,你拥有一种紧凑高效的实现目标的方法。另一方面,你的团队中其他人可能无法理解它,这使得将来很难进行维护。

我的一般规则是询问所使用的技术是否是合理的,可以期望程序员普遍掌握。在这种情况下,我认为合理期望程序员知道如何使用布尔运算符,因此在if语句中使用异或操作是可以接受的。

一个不适当的例子是使用异或操作交换两个变量而不使用临时变量的技巧。我不希望每个人都熟悉这个技巧,因此它不能通过代码审查。


14

我认为如果你评论一下它,例如 // ^ == 异或,也没什么问题。


10

你可以将其包装在一个函数中,以便为其提供一个详细的名称:

public static boolean XOR(boolean A, boolean B) {
    return A ^ B;
}

但是,在我看来,对于不知道 ^ 操作符用途的任何人来说,通过谷歌很快就能找到答案。第一次记住它后,再也不难了。由于你请求其他用途,使用异或(XOR)进行位掩码处理是很常见的。

您还可以使用 XOR 在不使用第三个临时变量的情况下交换两个变量的值。

// Swap the values in A and B
A ^= B;
B ^= A;
A ^= B;

这里有一个关于异或交换的Stackoverflow问题


6
if((boolean1 && !boolean2) || (boolean2 && !boolean1)) 
{ 
  //do it 
} 

在我看来,这段代码可以简化:

if(boolean1 != boolean2) 
{ 
  //do it 
} 

5

考虑到代码的清晰性,我认为在布尔检查中使用异或运算符并不是异或位运算符的典型用法。根据我的经验,在Java中,按位异或通常用于实现掩码标志切换行为:

flags = flags ^ MASK;

这篇文章由Vipan Singla撰写,更详细地解释了使用情况。

如果您需要像您的示例中一样使用位异或运算符,请注明为什么使用它,因为即使是熟悉位运算的受众也很可能需要停下来理解为什么要使用它。


0

个人而言,我更喜欢使用"boolean1 ^ boolean2"表达式,因为它更加简洁。

如果我处于你的情况(在一个团队中工作),我会通过将"boolean1 ^ boolean2"逻辑封装在一个带有描述性名称的函数中来做出妥协,比如说"isDifferent(boolean1, boolean2)"。

举个例子,你可以像这样调用"isDifferent(boolean1, boolean2)"来代替使用"boolean1 ^ boolean2":

if (isDifferent(boolean1, boolean2))
{
  //do it
}

你的“isDifferent(boolean1, boolean2)”函数应该长这样:
private boolean isDifferent(boolean1, boolean2)
{
    return boolean1 ^ boolean2;
}

当然,这个解决方案涉及到使用一个表面上多余的函数调用,本身也要受到最佳实践审查的影响,但它避免了冗长(丑陋)的表达式“(boolean1 && !boolean2) || (boolean2 && !boolean1)”!

-2
作为一种位运算符,异或比任何其他替代方法都要快得多。因此,在对性能至关重要且可扩展的计算中,异或是必不可少的。
我的主观个人意见:绝对禁止出于任何目的使用等号(==或!=)来处理布尔值。使用它显示了缺乏基本的编程伦理和基础知识。任何对^感到困惑的人都应该被送回布尔代数的基础知识(我很想在这里写“到信仰的河流” :))。

1
除了JIT在关键点(小)优化方面非常出色,例如用另一个布尔表达式替换一个布尔表达式。 - David Leppik
1
此外,^ 不是主要的布尔(逻辑)运算符,而是位运算符。它告诉读者要放慢速度,因为可能会有符号错误。如果你使用 ^ 代替 !=,如果你以后编写 C 代码,你会遇到很大的麻烦。位运算符是向读者(包括你自己在内的调试代码的人)发出的信号,让他们放慢速度并寻找符号错误。它们可能很棘手。例如,你知道 Java 的 % 不是真正的模数,就像 C 或 Python 中一样吗?我曾经有一段代码片段,在 C、JavaScript 和 Python 中运行相同,但在 Java 中却不同。 - David Leppik
6
这个怎么被点赞了呢?首先,在Java中,异或(XOR)和不等于(!=)被编译成[https://dev59.com/o2855IYBdhLWcg3wuG-W#4175512]相同的代码。其次,即使在汇编语言中,测试相等性和异或也是单一简单操作。你有任何数据支持你的说法吗? - jmiserez

-2

如果使用模式能够证明其合理性,为什么不呢?虽然你的团队一开始可能不认识这个运算符,但随着时间的推移,他们会学会的。人类时刻都在学习新词汇,为什么编程中不能呢?

我只想提醒一点,"^" 运算符没有你第二个布尔检查的短路语义。如果你真的需要短路语义,那么静态工具方法也可以实现。

public static boolean xor(boolean a, boolean b) {
    return (a && !b) || (b && !a);
}

16
使用异或运算符时,我不认为存在短路的可能性——你必须知道a和b才能计算出结果。 - Thelema
7
此外,这些参数将在调用时进行评估,因此不会发生任何短路行为。 - erikkallen
3
此外,异或运算应该在机器级别上是单个操作。 - Ogre Psalm33
你可能需要查一下短路求值和惰性求值的区别。短路求值是一种代码风格,可以防止调用会导致运行时错误的函数,例如除以零。在C语言中,可以使用´if (denominator != 0 && numerator / denominator)´来实现,它本身利用了惰性求值来防止除以零。你的回答也纯属推测。 - Martin
2
说实话,一个程序员写一个函数 xor,它以迂回的方式执行异或运算符所做的事情,会在我的脑海中引起更多问题(特别是关于能力方面)比一个只使用 ^ 的程序员。 - Stijn de Witt
这个答案毫无意义。考虑异或运算的4种可能输入。现在检查你复杂表达式中每种情况下会发生什么。当然,在所有4种情况下,都会在结果确定之前评估a和b。如果没有这样做,您将无法计算异或运算。除了不必要的复杂表达式没有完全评估之外,没有短路(当然)。 - ToolmakerSteve

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