为什么我们通常使用|| 超过|?有什么区别?


219

我只是想知道为什么我们通常||在两个布尔之间使用逻辑OR 而不是按位OR |,尽管它们都运行良好。

我的意思是,请看以下内容:

if(true  | true)  // pass
if(true  | false) // pass
if(false | true)  // pass
if(false | false) // no pass
if(true  || true)  // pass
if(true  || false) // pass
if(false || true)  // pass
if(false || false) // no pass

我们可以|代替使用||吗?与&和相同&&


16
大多数人忘记了 除了是按位运算符之外,它还是一个非短路布尔运算符。
John Meagher

1
有关差异的详细信息在JLS中。见java.sun.com/docs/books/jls/third_edition/html/...
约翰·米格尔

64
她们不一样。请查看有关它们的教程,特别是有关短路评估与急切评估的教程。||&&短路而|&渴望。
气垫船充满鳗鱼

4
出于好奇,您实际上想在什么情况下使用非短路版本?我几乎总是看&&||,但从来没有& |。如果您要做的事情取决于副作用,我不明白为什么要使用类似的东西,(a & b | c)因为有人很容易会想到“我可以通过使用短路版本来优化它”。
Mike Bailey

2
并且,当然,它们具有不同的优先级。
热门点击

Answers:


349

如果使用||&&形式,而不是这些运算符的|&形式,则Java不会费心地单独评估右手操作数。

多数情况下,是否要缩短评估是一个问题。

说明短路好处的一个好方法是考虑以下示例。

Boolean b = true;
if(b || foo.timeConsumingCall())
{
   //we entered without calling timeConsumingCall()
}

正如Jeremy和Peter提到的,短路的另一个好处是空引用检查:

if(string != null && string.isEmpty())
{
    //we check for string being null before calling isEmpty()
}

更多信息


115
典型的例子是foo != null && foo.hasBar()
Jeremy

1
如果使用|添加有关可能的空引用异常的信息 来自@Jeremy的评论,那么这是一个很好的答案。
彼得·凯利

还请记住&&和|| 是指在机器代码级别执行分支指令(记住分支可能会导致分支预测错误),因此,如果您对性能超级感兴趣,请仅在实际需要时(foo != null && foo.hasBar()或更快b || foo.timeConsumingCall())使用它们。不过,有99%的开发人员无需担心此级别的微优化。
乔纳森·迪金森

3
我很惊讶没有人提到要使用|的情况。我最常使用的场景是在检查(j> 3 | ++ i> 3)或(++ i> 3 | modifyGlobalAmongOtherThings()= true)时修改变量。虽然不太常见。
2011年

8
另一个典型的例子是string == null || string.isEmpty();)
彼得·劳瑞

83

| 不对布尔表达式进行短路评估。||将停止评估第一个操作数是否为true,但|不会。

另外,|可用于对字节/短/整数/长数值执行按位或运算。||不能。


提供完整的答案,我会接受的。到目前为止,您是第一个了解这一方面的人。
John Meagher

缺少|的按位方面
John Meagher

63

因此,仅以其他答案为例,在以下防御性检查中,短路至关重要:

if (foo == null || foo.isClosed()) {
    return;
}

if (bar != null && bar.isBlue()) {
    foo.doSomething();
}

使用|&可能会导致NullPointerException被抛出此处。


如果应用了NullObject模式,则不会(或者反而会否定答案)。另外,我会说检查foo是否为蓝色是foo内部的内容。如果是蓝色,则doSomething应该什么也不做。
nicodemus13 2011年

@ nicodemus13-好点,尽管有时仅需要使用Null Object模式,并且主体可能是另一个对的调用foo。彼得·劳瑞(Peter Lawrey)的“典范案例”是最好的。
Paul Bellora 2011年

@Khan:是的,我有点生气,并且Null Object并不总是合适的。我只是养成了潜意识地重构事物的习惯。您的答案没有什么特别的错误。
nicodemus13 2011年

39

逻辑||&&仅在必要时检查右侧。该|&检查两侧每次。

例如:

int i = 12;
if (i == 10 & i < 9) // It will check if i == 10 and if i < 9
...

重写它:

int i = 12;
if (i == 10 && i < 9) // It will check if i == 10 and stop checking afterward because i != 10
...

另一个例子:

int i = 12;
if (i == 12 | i > 10) // It will check if i == 12 and it will check if i > 10
...

重写它:

int i = 12;
if (i == 12 || i > 10) // It will check if i == 12, it does, so it stops checking and executes what is in the if statement
...

18

还要注意一个常见的陷阱:非惰性运算符的优先级高于惰性运算符,因此:

boolean a, b, c;
a || b && c; //resolves to a || (b && c)
a | b && c; //resolves to (a | b) && c

混合时要小心。


15

除了短路外,还要记住的另一件事是,对可能不是0或1的值进行按位逻辑运算与条件逻辑的含义非常不同。虽然通常对于|||,和&和而言是相同的,但&&结果却大不相同(例如2 & 4,0 / false,而2 && 41 / true)。

如果您从函数中获取的内容实际上是错误代码,并且正在测试非零度,那么这可能会非常重要。

在Java中,这不是一个大问题,在Java中,您必须显式地将其类型转换为布尔值或与0或类似值进行比较,但是在其他具有类似语法的语言中(C / C ++等),这可能会造成很大的混乱。

另外,请注意&和| 仅适用于整数类型的值,不适用于布尔测试的所有值。同样,在非Java语言中,有很多东西可以用作带有隐式!= 0比较的布尔值(指针,浮点数,带有的对象operator bool()等),在这些情况下按位运算符几乎总是无意义的。


3
我很高兴至少有人提到了按位运算符存在的全部目的。
ulidtko 2011年

9

只有当您拥有非常简单的布尔表达式并且捷径(例如,一个分支)的开销大于不评估后续表达式而节省的时间时,您才会使用|&代替||&&

但是,这是一个微优化,只有在最底层的代码中才很少发生。


1
在某些情况下,编译器是否自动执行此操作将很有趣。
starblue 2011年

JIT也许可以,但是编译器往往只处理简单的优化。
彼得·劳瑞

2
是的,我也看到过以下情况:比||的分支开销快得多,尤其是在没有或没有分支预测的CPU上。这是罕见的,但并非闻所未闻。我的一位同事与某承包商在某些代码中陷入了退战,因为他(正确地)使用了|。承包商一直认为这是“错误的”。
蓬松的

4
@Fluffy,故事的寓意是,如果您做一些棘手的事情,它需要评论您为什么这样做,否则您的工作以后可能会浪费掉。;)
Peter Lawrey 2011年

1
是的,最终他添加了一条评论(在我的建议下,如何让承包商停止“修复”),一切都很好。
蓬松的

8

|| 是|时的逻辑或运算符 是按位或运算符。

boolean a = true;
boolean b = false;

if (a || b) {
}

int a = 0x0001;
a = a | 0x0002;

1
缺少| 也是一个非短路布尔运算符。
John Meagher

2
@John Meagher:这是隐式的,因为它是按位的
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

@ L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳您是如何在个人资料中使自己的名字与众不同的?
UdayKiran Pulipati

8

一个| B:在对b求任何情况下,

|| b:仅当 a的值为假时才对b 求值


7

除了以下事实:是按位运算符:|| 是短路运算符-当一个元素为假时,它将不检查其他元素。

 if(something || someotherthing)
 if(something | someotherthing)

如果为真,|| 不会评估其他内容,而| 会做。如果if语句中的变量实际上是函数调用,请使用|| 可能会节省很多性能。


为什么要使用| 在if语句中。|| 是布尔值,| 不是,仅当您已经在使用两个布尔值时才是布尔值。
FlySwat

这是获得全部答案的第一个答案。
John Meagher

这个答案是不正确的。如果为FALSE,则两个运算符都将继续下一个操作数。仅当第一个操作数为true时,才产生差异。
迈克尔·迈尔斯

更糟糕的是,它有一个绝对荒谬的例子。
FlySwat

我不知道为什么有人会使用| 或&在if语句中进行简单的布尔比较,但这是完全合法的,当我开始学习编程时,我实际上已经看到了这样的示例。
Michael Stum


3

这些运算符||&&称为条件运算符,而|&称为按位运算符。它们有不同的用途。

条件运算符仅适用boolean于在左侧和右侧均静态求和的表达式。

按位运算符可用于任何数字操作数。

如果要执行逻辑比较,则应使用条件运算符,因为您将在代码中添加某种类型的安全性。


嗯,|并且&也是有条件的经营者。请查看我的评论中指向原始帖子的链接。
气垫船充满鳗鱼

@气垫船充满鳗鱼:该图表有点误导;它仅在布尔值的上下文中将它们称为条件运算符,在数学上它们等同于急切的逻辑运算符。当您开始处理值不是0或1或浮点值或指针之类的东西时,比较就会失败。
蓬松的

@fluffy:关于图表没有任何误导,因为讨论仅涉及布尔运算符。该|&可以用作位运算符是一个完全独立的问题。
气垫船充满鳗鱼,

1
将它们称为对布尔值使用的按位运算符,而不是布尔运算符会更准确。当只有一点点时,它们只是在数学上相等。
蓬松的

2

附带说明:Java有| =但没有|| =

何时必须使用||的示例 是第一个表达式是测试以查看第二个表达式是否会崩溃的时间。例如,使用单个| 在以下情况下,可能会导致NPE。

public static boolean isNotSet(String text) {
   return text == null || text.length() == 0;
}

2

其他答案已经很好地解决了运算符之间的功能差异,但是答案可能适用于当今存在的几乎每种C衍生语言。问题被标记为,因此我将尽一切努力专门针对Java语言进行回答。

&并且|可以是整数位运算符或布尔逻辑算子。按位和逻辑运算符(第15.22节)的语法为:

AndExpression:
  EqualityExpression 
  AndExpression & EqualityExpression

ExclusiveOrExpression:
  AndExpression 
  ExclusiveOrExpression ^ AndExpression

InclusiveOrExpression:
  ExclusiveOrExpression 
  InclusiveOrExpression | ExclusiveOrExpression

的语法EqualityExpression中定义§15.21,这需要RelationalExpression在定义§15.20,这又需要ShiftExpressionReferenceType所定义§15.19§4.3分别。ShiftExpression要求AdditiveExpression中所定义§15.18,继续向下钻取,限定基本的算术运算,一元运算符等ReferenceType深入到所有不同的方式来表示一个类型。(虽然ReferenceType不包括基本类型,基本类型的定义最终需要的,因为它们可以是用于一个阵列,其尺寸类型一个ReferenceType)。

按位和逻辑运算符具有以下属性:

  • 这些运算符具有不同的优先级,&具有最高优先级和|最低优先级。
  • 这些运算符中的每一个在语法上都是左关联的(每个组从左到右)。
  • 如果操作数表达式没有副作用,则每个运算符都是可交换的。
  • 每个运算符都是关联的。
  • 按位和逻辑运算符可用于比较两个数字类型的操作数或两个类型的操作数boolean。所有其他情况都会导致编译时错误。

运算符是按位运算符还是逻辑运算符之间的区别取决于操作数是“可转换为原始整数类型”(第4.2节)还是它们是boolean或类型Boolean(第5.1.8节)。

如果操作数是整数类型,则对两个操作数都执行二进制数值提升(第5.6.2节),而将它们都保留为longs或ints。操作的类型将是(提升的)操作数的类型。此时,&将是按位与,^将是按位异或,并且|将是按位或。(§15.22.1

如果操作数为booleanor Boolean,则在必要时将对操作数进行拆箱转换(第5.1.8节),并且操作类型为boolean&将导致true如果两个操作数都是true^将导致true在两个操作数是不同的,并且|将导致true如果操作数是true。(§15.22.2

相反, &&是“ Conditional-And运算符”(第15.23节),||是“ Conditional-Or运算符”(第15.24节)。它们的语法定义为:

ConditionalAndExpression:
  InclusiveOrExpression 
  ConditionalAndExpression && InclusiveOrExpression

ConditionalOrExpression:
  ConditionalAndExpression 
  ConditionalOrExpression || ConditionalAndExpression

&&就像一样&,不同的是它仅在左操作数为时评估右操作数true||就像一样|,不同的是它仅在左操作数为时评估右操作数false

有条件的-并且具有以下属性:

  • 条件和运算符在语法上是左关联的(它从左到右分组)。
  • 条件和运算符在副作用和结果值方面是完全相关的。也就是说,对于任何表达式ab和和c对该表达式的求值都会((a) && (b)) && (c)产生与该表达式的求值相同的结果,并且以相同的顺序发生相同的副作用(a) && ((b) && (c))
  • 条件和运算符的每个操作数必须为boolean或类型Boolean,否则会发生编译时错误。
  • 条件和表达式的类型始终为boolean
  • 在运行时,首先对左侧操作数表达式求值;如果结果具有type Boolean,则将其进行拆箱转换(第5.1.8节)。
  • 如果结果值为false,则条件和表达式的值为,false并且不计算右侧操作数表达式。
  • 如果左侧操作数的值为true,则对右侧表达式求值;否则,将求值。如果结果具有type Boolean,则将其进行拆箱转换(第5.1.8节)。结果值成为条件和表达式的值。
  • 因此,&&计算相同的结果&boolean的操作数。区别仅在于,右侧操作数表达式是有条件而不是始终进行求值的。

条件-或具有以下属性:

  • 条件或运算符在语法上是左联想的(它从左到右分组)。
  • 条件或运算符在副作用和结果值方面是完全关联的。也就是说,对于任何表达式ab和和c对该表达式的求值都会((a) || (b)) || (c)产生与该表达式的求值相同的结果,并且以相同的顺序发生相同的副作用(a) || ((b) || (c))
  • 条件或运算符的每个操作数都必须为boolean或类型Boolean,否则会发生编译时错误。
  • 条件或表达式的类型始终为boolean
  • 在运行时,首先对左侧操作数表达式求值;如果结果具有type Boolean,则将其进行拆箱转换(第5.1.8节)。
  • 如果结果值为true,则条件或表达式的值为,true并且不计算右侧操作数表达式。
  • 如果左侧操作数的值为false,则对右侧表达式求值;否则,将求值。如果结果具有type Boolean,则将其进行拆箱转换(第5.1.8节)。结果值成为条件或表达式的值。
  • 因此,||计算与|on booleanBoolean操作数相同的结果。区别仅在于,右侧操作数表达式是有条件而不是始终进行求值的。

总之,作为@JohnMeagher曾多次在评论中指出,&|,事实上,非短路的操作数是任何的具体情况布尔运算符booleanBoolean。如果采用良好的做法(即没有次要影响),这是一个很小的差异。但是,当操作数不是booleans或Booleans时,运算符的行为大不相同了:在Java编程的高级水平上,按位运算和逻辑运算根本无法很好地进行比较。


2

1)。(表达式1 |表达式2),| 无论expression1的结果是true还是false,运算符都将对expression2求值。

例:

class Or 
{
    public static void main(String[] args) 
    {
        boolean b=true;

        if (b | test());
    }

    static boolean test()
    {
        System.out.println("No short circuit!");
        return false;
    }
}

2)。(expression1 || expression2),|| 如果expression1为true,则运算符不会计算expression2。

例:

class Or 
{
    public static void main(String[] args) 
    {
        boolean b=true;

        if (b || test())
        {
            System.out.println("short circuit!");
        }
    }

    static boolean test()
    {
        System.out.println("No short circuit!");
        return false;
    }
}

1

|| 通过对两个值进行“或”运算来返回布尔值(这就是为什么将其称为LOGICAL或)

IE浏览器:

if (A || B) 

如果A或B为true,则返回true,否则均为false。

| 是对两个值执行按位运算的运算符。为了更好地理解按位运算,可以在这里阅读:

http://en.wikipedia.org/wiki/Bitwise_operation


1

主要区别在于|| 和&&表现出“短路”,因此仅在需要时才评估RHS。

例如

if (a || b) {
    path1...
} else {
    path2..
}

在上面,如果a为true,则将不测试b并执行path1。如果| 被使用,那么即使'a'为真,也将评估双方。

有关更多信息,请参见此处此处

希望这可以帮助。


1

非短路可能很有用。有时您要确保两个表达式求值。例如,假设您有一个从两个单独的列表中删除对象的方法。您可能想要执行以下操作:

class foo {

    ArrayList<Bar> list1 = new ArrayList<Bar>();
    ArrayList<Bar> list2 = new ArrayList<Bar>();

    //Returns true if bar is removed from both lists, otherwise false.
    boolean removeBar(Bar bar) {
        return (list1.remove(bar) & list2.remove(bar));
    }
}

如果您的方法改用条件操作数,则如果第一个列表返回false,则它将无法从第二个列表中删除对象。

//Fails to execute the second remove if the first returns false.
boolean removeBar(Bar bar) {
    return (list1.remove(bar) && list2.remove(bar));
}

它并不是非常有用,并且(与大多数编程任务一样)您可以通过其他方式实现它。但这是按位操作数的用例。


1

它们之间的基本区别是| 首先将值转换为二进制,然后执行按位运算。同时|| 不会将数据转换为二进制,而只是对原始状态执行or表达式。

int two = -2; int four = -4;
result = two | four; // bitwise OR example

System.out.println(Integer.toBinaryString(two));
System.out.println(Integer.toBinaryString(four));
System.out.println(Integer.toBinaryString(result));

Output:
11111111111111111111111111111110
11111111111111111111111111111100
11111111111111111111111111111110

了解更多:http : //javarevisited.blogspot.com/2015/01/difference-between-bitwsie-and-logical.html#ixzz45PCxdQhk


当操作数是布尔值和愚蠢的格式时,则为true。
罗恩侯爵,

2
这有助于我理解Long.valueOf(100 | 200)= 236的原因。原因如下:0 1 1 0 0 1 0 0 | 1 1 0 0 1 0 0 0 = 1 1 1 0 1 1 0 0 = 128 64 32 0 8 4 0 0 = 236
donlys

1

当我遇到这个问题时,我创建了测试代码来了解这一点。

public class HelloWorld{

   public static boolean bool(){
      System.out.println("Bool");
      return true;
   }

   public static void main(String []args){

     boolean a = true;
     boolean b = false;

     if(a||bool())
     {
        System.out.println("If condition executed"); 
     }
     else{
         System.out.println("Else condition executed");
     }

 }
}

在这种情况下,我们仅更改if条件添加a或b的左侧值。

|| 场景,当左侧为真时[if(a || bool())]

输出 "If condition executed"

|| 场景,当左侧为假[if(b || bool())]

输出-

Bool
If condition executed

Conclusion of || 使用时||,右侧仅检查左侧为false时。

| 场景,当左侧为真时[if(a | bool())]

输出-

Bool
If condition executed

| 场景,当左侧为假[if(b | bool())]

输出-

Bool
If condition executed

Conclusion of | 使用时|,请同时检查左右两侧。



0

通常我在有前递增和后递增运算符时使用。看下面的代码:

package ocjpPractice;
/**
 * @author tithik
 *
 */
public class Ex1 {

    public static void main(String[] args) {
    int i=10;
    int j=9;
    int x=10;
    int y=9;
    if(i==10 | ++i>j){
        System.out.println("it will print in first if");  
        System.out.println("i is: "+i);
    }

    if(x==10 ||++x>y){
        System.out.println("it will print in second if");   
        System.out.println("x is: "+x);
    }
    }
}

输出:

如果
i为:11

将首先打印;如果
x为:10 将第二个打印。

两个if块都相同,但结果不同。如果存在|,将同时评估两个条件。但是,如果为||,则不会评估第二个条件,因为第一个条件已经为真。


1
我觉得这很令人困惑
NimChimpsky 2013年

0

有许多用例表明了为什么要去||而不是去|。一些用例必须使用|运算符来检查所有条件。

例如,如果您要检查表单验证,并且想向用户显示所有带有错误文本的无效字段,而不仅仅是第一个无效字段。

|| 运算符是

   if(checkIfEmpty(nameField) || checkIfEmpty(phoneField) || checkIfEmpty(emailField)) {
      // invalid form with one or more empty fields
   }

   private boolean checkIfEmpty(Widget field) {
      if(field.isEmpty()) {
        field.setErrorMessage("Should not be empty!");
        return true;
      }
      return false;
   }

因此,使用以上代码段,如果用户提交的表单中包含所有空白字段,则nameField只会显示错误消息。但是,如果将其更改为

   if(checkIfEmpty(nameField) | checkIfEmpty(phoneField) | checkIfEmpty(emailField)) {
      // invalid form with one or more empty fields
   }

无论true条件如何,它将在每个字段上显示正确的错误消息。


0

仔细阅读本主题后,对于|用作逻辑运算符是否符合Java模式惯例的问题,我仍然不清楚。

我最近在请求中的注释中修改了代码,其中

if(function1() | function2()){
  ...
}

必须更改为

boolean isChanged = function1();
isChanged |= function2();
if (isChanged){
  ...
}

实际接受的版本是什么?

Java文档是|为一个逻辑非短路或运算。

对投票不感兴趣,但是对找出标准不感兴趣?两种代码版本都可以按预期进行编译和运行。





-2

| 是按位运算符。|| 是逻辑运算符。

一个将占用两位或两位。

一个人将确定真相(这个或那个)。如果这是正确的或正确的,那么答案是正确的。

哦,当当的人很快回答了这些问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.