48

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

我知道它们是如何工作的,但我想不出一个&值得使用的情况。

4

11 回答 11

61

我在现实生活中发现了表达式的两边都非常便宜的情况,因此它缩短了一两纳秒以避免分支并使用无条件&而不是&&. (不过,这些都是非常高性能的数学实用程序;我几乎不会在其他代码中使用它,而且如果没有详尽的基准测试来证明它更好,我也不会这样做。)

(举个具体的例子,x > 0将是超级便宜且无副作用的。为什么还要冒着分支错误预测的风险来避免无论如何都会如此便宜的测试?当然,因为这是boolean最终结果将被使用无论如何在一个分支中,但if (x >= 0 && x <= 10)涉及两个分支,并且if (x >= 0 & x <= 10)只涉及一个。)

于 2012-07-10T11:15:36.423 回答
20

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

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

适用于&&,但&如果 a 为空,您可能会得到 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
}

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

于 2012-07-10T11:10:47.703 回答
15

&& 允许 jvm 进行短路评估。也就是说,如果第一个参数为假,则不需要检查第二个参数。

一个 & 将在两边同时运行。

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

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

在该示例中,您可能总是希望记录帐户所有者试图做某事的事实。

我不认为我曾经在商业编程中使用过单个 & 。

于 2012-07-10T11:11:53.303 回答
11

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

您更喜欢哪里的非短路运算符?

从同一个链接:

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

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

于 2012-07-10T11:23:34.490 回答
7

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

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

于 2012-07-10T11:13:27.353 回答
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 行。与往常一样,这是哪种权衡最适合您的情况。

于 2012-07-10T13:04:15.770 回答
6

&如果表达式很简单,您可以通过使用或|阻止分支来获得微优化。IE。

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

是相同的

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

它有两个地方可以出现一个分支。

然而,使用无条件的&or |,只能有一个分支。

这是否有帮助很大程度上取决于代码在做什么。

如果您使用它,我建议对其进行评论,以非常清楚地说明为什么这样做。

于 2012-07-10T11:39:15.623 回答
5

单个 & 没有任何特定用途,但您可以考虑以下情况。

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

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

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

于 2012-07-10T11:14:11.933 回答
3

因为&是按位运算符,您可以在单个操作中同时进行多达 32 次检查。对于这个非常具体的用例,这可以成为显着的速度增益。如果您需要检查大量条件,并且经常这样做并且装箱/拆箱条件的成本由检查次数分摊,或者如果您以该格式将数据存储在磁盘和 RAM 上(它在单个位掩码中存储 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) {
        ...;
    }
}

有关该主题的更多信息,请参阅我对相关问题的其他答案。

于 2012-07-11T03:11:41.860 回答
1

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

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

// ...

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

尽管您可以在没有以下情况下执行此操作&

if(x == y){/* ... */}
update();
于 2012-07-10T11:14:27.837 回答
1

短路会导致现代处理器的分支预测错误,并显着降低性能(一个显着的例子是高度优化的光线,光线追踪中的轴对齐框相交代码)[需要澄清]。

于 2012-07-25T08:24:57.400 回答