什么情况下最好使用无条件AND(而不是&&)

Lui*_*ano 48 java optimization

我想知道Java中的一些情况(或更一般地说:在编程中),当布尔表达式中首选使用unconditional AND(&)而不是条件版本(&&)时.

我知道它们是如何工作的,但我不能考虑使用单一的情况&是值得的.

Lou*_*man 60

我已经在现实生活中发现了表达式的两面都非常便宜的情况,所以它削减了一两秒或两个以避免分支并使用无条件&而不是&&.(但这些是非常高性能的数学工具;我几乎从不在其他代码中使用它,如果没有详尽的基准测试来证明它更好,我也不会这样做.)

(举一个具体的例子,x > 0将是超级廉价和无副作用.为什么要冒险分支错误预测,以避免测试会变得如此便宜?当然,因为它是boolean最终结果将被使用无论如何在一个分支,但if (x >= 0 && x <= 10)涉及两个分支,if (x >= 0 & x <= 10)只涉及一个.)

  • ......在我得到这么多赞成票的所有答案中,我都不会期望它是这一个.读者,你几乎应该在实际的代码中使用`&` - 当然不是没有至少一个小时的时间来为_each_开发微基准,你想把`&&'切换成`&`.我研究成千上万程序员使用的库,因此在这些情况下,微优化会得到回报,但几乎没有其他人应该做这些事情. (23认同)
  • `&&`涉及分支,当它决定是否评估第二个条件时.错误预测的分支在现代CPU上可能非常昂贵.`&`避开那个分支. (22认同)
  • @assylias:另请参阅[为什么处理排序的数组比未排序的数组更快?](http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-未分选的阵列) (7认同)
  • 在你的例子中@LouisWasserman都是相同的,因为没有可见的副作用,所以编译器可以自由地优化它.编译器生成不同字节码的唯一原因是javac几乎不进行优化--JIT编译器负责优化本机指令. (2认同)
  • 是的路易斯是对的.JIT确实无法优化`if(val> = 0xcafe && val <0xdead)` - gcc确实如此. (2认同)

ass*_*ias 20

唯一不同的是,&&||一旦它被称为停止评价.例如:

if (a != null && a.get() != null)
Run Code Online (Sandbox Code Playgroud)

适用于&&,但&如果a为null,则可能会得到NullPointerException.

我可以考虑你想要使用的唯一情况&是,如果第二个操作数有副作用,例如(可能不是最好的例子,但你明白了):

public static void main(String[] args) {
    int i = 1;
    if (i == 0 & ++i != 2) {
    }
    System.out.println(i); //2
    i = 1;
    if (i == 0 && ++i != 2) {
    }
    System.out.println(i); //1
}
Run Code Online (Sandbox Code Playgroud)

但是,这看起来像臭臭的代码(在这两种情况下).


小智 15

&&允许jvm进行短路评估.也就是说,如果第一个参数为false,那么它不需要费心检查第二个参数.

一个单一的&将运行双方无论如何.

所以,作为一个人为的例子,你可能会:

if (account.isAllowed() & logAccountAndCheckFlag(account))
    // Do something
Run Code Online (Sandbox Code Playgroud)

在该示例中,您可能始终要记录帐户所有者尝试执行某些操作的事实.

我不认为我曾经使用过单一的商业编程.


San*_*osh 11

维基百科很好地描述了短路评估

您更喜欢非短路运营商?

来自同一链接:

  • 未经测试的第二种情况会导致不良的副作用
  • 代码效率

短路可能导致现代处理器上的分支预测错误,并且显着降低性能(一个值得注意的例子是在光线跟踪中具有轴对齐盒交叉代码的高度优化的光线)[需要澄清].有些编译器可以检测到这种情况并发出更快的代码,但由于可能违反C标准,因此并不总是可行.高度优化的代码应该使用其他方式来执行此操作(例如手动使用汇编代码)


har*_*old 7

如果有必然发生的副作用,但这有点难看.

按位AND(&)主要用于 - 按位数学.


Dan*_*ely 7

输入验证是一种可能的情况.您通常希望在一次传递中将表单中的所有错误报告给用户,而不是在第一次传递之后停止,并强制他们重复单击提交,每次只收到一个错误:

public boolean validateField(string userInput, string paramName) {
   bool valid;
   //do validation 

   if (valid) {
       //updates UI to remove error indicator (if present)
       reportValid(paramName);   
   } else {
       //updates UI to indicate a problem (color change, error icon, etc) 
       reportInvalid(paramName);  
   }      
}

public boolean validateAllInput(...) {

   boolean valid = true;
   valid = valid & validateField(userInput1, paramName1);
   valid = valid & validateField(userInput2, paramName2);
   valid = valid & validateField(userInput3, paramName3);
   valid = valid & validateField(userInput4, paramName4);
   valid = valid & validateField(userInput5, paramName5);

   return valid;
}

public void onSubmit() {

   if (validateAllInput(...)) {
       //go to next page of wizard, update database, etc
       processUserInput(userInput1, userInput2, ... );
   } 

}

public void onInput1Changed() {
   validateField(input1.Text, paramName1);
}


public void onInput2Changed() {
   validateField(input2.Text, paramName2);
}

...
Run Code Online (Sandbox Code Playgroud)

当然,你可以validateAllInput()通过重构if (valid) { reportValid() ...逻辑来避免短路评估的需要validateField(); 但是每次调用validateField()时你都需要调用提取的代码; 至少为方法调用添加10行.一如既往,这是一种权衡取舍最适合您的情况.

  • @OrangeDog如果我填写一个包含N个错误的表单并单击提交,我宁愿被告知我需要修复X,Y和Z.它比用户友好更多:点击提交,得到关于X的错误,修复它,再次点击提交,得到关于Y的错误.... (2认同)

Pet*_*rey 6

如果表达式是微不足道的,您可以通过使用&或者|您正在阻止分支来进行微优化.即.

if(a && b) { }
if(!(a || b)) { }
Run Code Online (Sandbox Code Playgroud)

是相同的

if (a) if (b) { }
if (!a) if (!b) { }
Run Code Online (Sandbox Code Playgroud)

它有两个地方可以发生分支.

但是使用无条件&或者|,只能有一个分支.

这有用与否有很大程度上取决于代码的作用.

如果你使用它,我会对它进行评论,以便明确说明为什么要这样做.


Pra*_*iya 5

单个&没有任何具体用途,但您可以考虑以下情况.

if (x > 0 & someMethod(...))
{
  // code...
}
Run Code Online (Sandbox Code Playgroud)

考虑这someMethod()是做一些操作,它将修改实例变量或做一些会在以后的处理中影响行为的操作.

因此,在这种情况下,如果您使用&&运算符并且第一个条件失败,它将永远不会进入someMethod().在这种情况下,单个&运算符就足够了.