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

时间:2012-07-10 11:02:30

标签: java optimization

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

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

11 个答案:

答案 0 :(得分:60)

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

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

答案 1 :(得分:20)

唯一的区别是&&||一旦知道就会停止评估。例如:

if (a != null && a.get() != null)

适用于&&,但对于&,如果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
}

然而,对我来说这看起来像是臭臭的代码(在这两种情况下)。

答案 2 :(得分:15)

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

单身&amp;无论如何都会双方都这样做。

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

if (account.isAllowed() & logAccountAndCheckFlag(account))
    // Do something

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

我不认为我曾经使用过单曲&amp;但是在商业节目中。

答案 3 :(得分:11)

维基百科很好地描述了Short Circuit Evaluation

您更喜欢非短路运营商?

来自同一个链接:

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

短路可能导致现代分支预测错误   处理器,并大大降低性能(一个值得注意的例子是   高度优化的光线,光线中的轴对齐框交叉代码   追查)[需要澄清]。一些编译器可以检测到这种情况   并发出更快的代码,但由于可能,并不总是可能的   违反C标准。高度优化的代码应该使用其他代码   这样做的方法(如汇编代码的手动使用)

答案 4 :(得分:7)

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

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

答案 5 :(得分: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);
}

...

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

答案 6 :(得分:6)

如果表达式很简单,您可以使用&|进行微优化,因为您正在阻止分支。即

if(a && b) { }
if(!(a || b)) { }

相同
if (a) if (b) { }
if (!a) if (!b) { }

有两个地方可以发生分支。

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

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

如果您使用此功能,我会对其进行评论,以便明确说明原因。

答案 7 :(得分:5)

没有任何特定用途的单一&amp;但你可以考虑以下情况。

if (x > 0 & someMethod(...))
{
  // code...
}

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

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

答案 8 :(得分:3)

因为&是一个逐位运算符,所以您可以同时在一个操作中进行多达32次检查。对于这种非常具体的用例,这可以成为显着的速度增益。如果你需要检查大量的条件,并经常这样做,装箱/拆箱的成本是按支票的数量摊销的,或者你是以那种格式存储你的数据在磁盘和内存上(它在单个位掩码中存储32个条件的空间效率更高,&运算符可以比一系列32个单&&提供巨大的速度优势。例如,如果你想选择所有可以移动的单位,步兵,武器升级,并由玩家3控制,你可以这样做:

int MASK = CAN_MOVE | INFANTRY | CAN_ATTACK | HAS_WEAPON_UPGRADE | PLAYER_3;
for (Unit u in allunits) {
    if (u.mask & MASK == MASK) {
        ...;
    }
}

有关该主题的更多信息,请参阅my other answers相关问题。

答案 9 :(得分:1)

我能想到的唯一好处是,当您需要调用方法或执行代码时,无论第一个表达式是否被评估为truefalse

public boolean update()
{
    // do whatever you want here
    return true;
}

// ...

if(x == y & update()){ /* ... */}

虽然您可以在没有&的情况下执行此操作:

if(x == y){/* ... */}
update();

答案 10 :(得分:1)

短路可能导致现代处理器上的分支预测错误,并显着降低性能(一个值得注意的例子是高度优化的光线,在光线跟踪中使用轴对齐的盒子交叉代码)[需要澄清]。