我为什么要经常使用||而不是|和&&而不是&?

时间:2013-11-22 19:08:15

标签: java boolean operators bit-manipulation conditional-statements

自从我开始编写Java以来​​,我注意到每个人都在使用&&||而不是&|。这是什么原因?我一直在使用&&||,因为我不知道你可以在布尔值上使用&|

class A{static{
  boolean t = true;
  boolean f = false;
  System.out.println("&ff " + (f&&f) + " " + (f&f));
  System.out.println("&ft " + (f&&t) + " " + (f&t));
  System.out.println("&tf " + (t&&f) + " " + (t&f));
  System.out.println("&tt " + (t&&t) + " " + (t&t));
  System.out.println("|ff " + (f||f) + " " + (f|f));
  System.out.println("|ft " + (f||t) + " " + (f|t));
  System.out.println("|tf " + (t||f) + " " + (t|f));
  System.out.println("|tt " + (t||t) + " " + (t|t));
}}

据我所知,它们是一样的:

$ javac A.java && java A
&ff false false
&ft false false
&tf false false
&tt true true
|ff false false
|ft true true
|tf true true
|tt true true
Exception in thread "main" java.lang.NoSuchMethodError: main

使用||&&以任何方式改进我的代码吗?


作为一个简单的测试,我用短格式替换了数百次,并且我的所有单元测试仍然通过。

9 个答案:

答案 0 :(得分:12)

||&&使用short circuit evaluation

来自same article

  

短路评估,最低评估或麦卡锡评估   表示某些编程中某些布尔运算符的语义   仅执行或评估第二个参数的语言   如果第一个参数不足以确定的值   表达

考虑到你有一个对象,如果你这样做,你想要检查一个属性的某些值:

if(obj != null & obj.ID == 1)

如果您的对象obj为空,您将获得一个空引用异常,因为您使用了单&,第一个条件评估为false,但它仍将继续到下一个条件和提高空引用异常。

如果您使用&&,那么第二个条件永远不会被评估,因此没有例外。

if(obj != null && obj.ID == 1)

使用您的代码,您将看不到任何差异,但使用具有多个条件的按位|&将导致完全没有短路。

更多解释。


考虑您遵循以下代码:

Boolean conditionA = 2 > 1; //some condition which returns true
if(conditionA | obj.SomeTimeConsumingMethod()){

}

现在在上面的代码段中,假设您有一个方法SomeTimeConsumingMethod的对象,这会花费大量时间处理并返回truefalse。如果您使用单个|,则会评估这两个条件,因为首先conditionAtrue它也会处理obj.SomeTimeConsumingMethod。由于使用了true (OR),因此整个if语句的最终结果为|

如果你的条件是使用双|| (或逻辑运算符)

if(conditionA || obj.SomeTimeConsumingMethod())

然后将不评估第二个条件obj.SomeTimeConsumingMethod()。这是短路的,这使您无法执行一些耗时的方法。无论true返回什么,最终结果仍为obj.SomeTimeConsumingMethod()

答案 1 :(得分:3)

||是合乎逻辑的,或者|是按位运算或。与&&&相同。如果您使用&&语句,请使用||if;如果您正在执行位操作,请使用|&

答案 2 :(得分:2)

正如已经指出的那样,&&||进行短路评估。带有布尔操作数的&|执行相同的操作,但无论第一个操作数的值如何,都要计算两个操作数。

在某些情况下,您使用它很重要,例如当左操作数是右手操作数的无异常评估的前提条件时。

在大多数情况下,没关系。在这些情况下,我使用&&||来提高可读性,因为这是常见的选择。将&|与布尔操作数一起使用会使大多数Java程序员不得不停下来思考,并问自己是否有某些特定的原因。我怀疑它可能是如此常见,因为C和C ++的继承。

答案 3 :(得分:1)

虽然您可以使用逐位运算符获得“正确”结果,但要了解它们并不代表相同的事情。例如,这可行

public static void main(String[] args){
    int x = 5;
    int y = 9;
    System.out.println(x&y); // prints "1"
} 

但是,这不会

public static void main(String[] args){
    int x = 5;
    int y = 9;
    System.out.println(x&&y); // Compile error: java: operator && cannot 
                              //                be applied to int,int
}

你没有对布尔逻辑使用逐位运算符的原因是它清楚了解代码尝试做什么(以及知道它实际上有效)。逐位运算符用于操作位值,其中布尔运算符用于评估一阶逻辑语句。

虽然不一样,但使用.equals()==进行比较的原因相同 - 您不希望读者猜测您的意图。 可能在某些情况下工作(例如将常量,硬编码的字符串与==进行比较),但它的形式很差,因为它所说的就是你是要求实例平等而不是价值平等,这通常是隐含的。使用逐位运算符意味着您需要进行位操作,而不是一阶逻辑评估。

答案 4 :(得分:1)

认为短路评估是强制性的,因为性能很差。你可以提出一个几乎同样弱的论点:短路评估会鼓励额外的非确定性和侧面渠道。随机优化或随机丢失非确定性/边通道,我更喜欢后者。

唯一剩下的论点是:如果你的代码库依赖于短路行为,例如Habib(if (obj != null && obj.ID == 1))描述的技巧,那么在{{1}中开始混合可能不是一个好主意。因为程序员可能会把它与&混淆。也就是说,如果您在任何地方使用&& / &,但仅在您依赖短路评估的几个地方使用|,则有人可能会出错并只放{{1} ,可能很难发现。在这种情况下,如果您依靠&&进行性能而非&检查,则可能更难发现有人意外地单独放置&&,因为没有堆栈跟踪。

答案 5 :(得分:1)

  

使用||和&&以任何方式改进我的代码?

让我演示两个关于条件布尔运算符如何改进代码的例子。比较

if (a != null && a.equals("b") || z != null && z.equals("y"))
  System.out.println("correct");

boolean b1 = a != null, b2 = z != null;
if (b1) b1 = b1 & a.equals("b");
if (b2) b2 = b2 & z.equals("y");
if (b1 | b2) System.out.println("correct");

这是我认为完全依赖于逻辑布尔运算符的最好方法。

其次,假设您要执行两项测试:simpleTest()heavyTest(),其中后者涉及发出HTTP请求并解析响应。

if (simpleTest() & heavyTest()) System.out.println("success");
else throw new IllegalStateException();

并希望优化它。你还想做什么,这个:

if (simpleTest()) {
  if (heavyTest()) System.out.println("success");
  else throw new IllegalStateException("operation could not be completed");
} else throw new IllegalStateException("operation could not be completed"); 

这样:

boolean b = simpleTest();
if (b) b = b & heavyTest());
if (b) System.out.println("success");
else throw new IllegalStateException("operation could not be completed");

或者这个:

if (simpleTest() && heavyTest()) System.out.println("success");
else throw new IllegalStateException();

  

为什么我总是要使用||而不是|和&&而不是&?

你不应该总是使用它们,但你可以通过将它们作为默认来帮助自己,因为在99.9%的使用中它们不会受到伤害或者让你的代码更好。保留逻辑运算符,以确保您确实需要其语义的0.1%的情况。


本页其他地方已经提到,依赖于对右操作数的条件评估会产生违反直觉的代码。我拒绝接受这种关于直觉的观点,因为根据定义,直觉是一种学习的技能。它是一种通过进化赋予你的设施,可以在你经常遇到的情境中识别出一种模式,并迅速(并且下意识地)切入正确的结论。学习语言最重要的一个方面就是获得正确的直觉。语言特征是坏的,因为它与第一次看到它的人违反直觉是不合理的。

答案 6 :(得分:0)

在Java(和C / C ++,如果我没记错的话)单个“|”和“&”是逐位运算符,而不是逻辑运算符,它们用于完全不同的东西。

你使用“&&”和“||”对于布尔语句

你使用“&”和“|”对于位操作,给出以下二进制数

答案 7 :(得分:0)

不要让假设这么快。因为这不会编译:

  static boolean f(boolean a) {
    boolean x = (true|a) ? a : x;
    return x;
  }

Bro.java:6: variable x might not have been initialized
    boolean x = (true|a) ? a : x;
                           ^

这将:

  static boolean f(boolean a) {
    boolean x = (true||a) ? a : x;
    return x;
  }

答案 8 :(得分:0)

&安培;&安培;和||是捷径运营商。例如:

A && B 

如果条件A为假,则不会评估条件B

A || B

如果条件A为真,则不会评估条件B

您应始终使用快捷运算符进行多个条件评估。 |或者&用于逐位操作。