Подтвердить что ты не робот

В каких случаях лучше использовать безусловный AND (& вместо &&)

Я хотел бы знать некоторые случаи в Java (или, более широко: в программировании), когда в булевых выражениях предпочтительнее использовать безусловный AND (&) вместо условной версии (&&).

Я знаю, как они работают, но я не могу думать о случае, когда стоит использовать один &.

4b9b3361

Ответ 1

Я нашел случаи в реальной жизни, где обе стороны выражения были действительно дешевыми, поэтому он сбрил наносекунду или два, чтобы избежать ветки и использовать безусловный & вместо &&. (Это были чрезвычайно высокопроизводительные математические утилиты, хотя, я бы почти никогда не использовал это в другом коде, и я бы не сделал это в любом случае без исчерпывающего бенчмаркинга, чтобы доказать, что это лучше.)

(Чтобы дать конкретные примеры, x > 0 будет супер дешевым и без побочных эффектов. Зачем рисковать ошибочным предсказанием ветки, чтобы избежать теста, который будет так дешев? Конечно, поскольку это a boolean конечный результат все равно будет использоваться в ветке, но if (x >= 0 && x <= 10) включает две ветки, а if (x >= 0 & x <= 10) включает только один.)

Ответ 2

Единственное отличие заключается в том, что && и || останавливают оценку, как только она известна. Так, например:

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

хорошо работает с &&, но с & вы можете получить исключение NullPointerException, если a равно null.

Единственный случай, когда я могу думать о том, где вы хотите использовать &, - это, например, если второй операнд имеет побочный эффект (возможно, не лучший пример, но вы получаете точку):

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
}

Однако это выглядит как вонючий код для меня (в обоих случаях).

Ответ 3

& & позволяет jvm выполнять оценку короткого замыкания. То есть, если первый аргумент является ложным, тогда ему не нужно проверять второй аргумент.

Единый и будет запускать обе стороны независимо.

Итак, в качестве надуманного примера у вас может быть:

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

В этом примере вы всегда можете зарегистрировать тот факт, что владелец учетной записи попытался что-то сделать.

Я не думаю, что когда-либо использовал один и в коммерческом программировании.

Ответ 4

Википедия хорошо описала оценку короткого замыкания

Где вы предпочитаете операторы с коротким замыканием?

Из той же ссылки:

  • Невыполнение второго условия приводит к неэффективному побочному эффекту
  • Эффективность кода

Короткое замыкание может привести к ошибкам в прогнозировании ветвлений на современном процессоров и значительно снизить производительность (примечательным примером является высоко оптимизированный луч с кодом пересечения с выровненной осью в луче отслеживание) [требуется разъяснение]. Некоторые компиляторы могут обнаруживать такие случаи и испускать более быстрый код, но это не всегда возможно из-за возможного нарушения стандарта C. Высоко оптимизированный код должен использовать другие способы для этого (например, ручное использование кода сборки)

Ответ 5

Если есть побочные эффекты, которые должны произойти, но это немного уродливо.

Побитовое И (&) в основном полезно только для этого - побитовая математика.

Ответ 6

Проверка ввода - один из возможных случаев. Обычно вы хотите сообщить все ошибки в форме пользователю за один проход, а не останавливаться после первого и заставлять их повторно нажимать кнопку повторно и получать только одну ошибку каждый раз:

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 дополнительных строк для вызовов методов. Как всегда, это случай, когда компромисс лучше всего подходит вам.

Ответ 7

Если выражение тривиально, вы можете получить микро-оптимизацию с помощью & или | в том, что вы предотвращаете ветвь. то есть.

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

совпадает с

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

который имеет два места, которые могут иметь ветки.

Однако, используя безусловный & или |, может быть только одна ветка.

Это помогает или не сильно зависит от того, что делает код.

Если вы используете это, я sugegst комментируя его, чтобы он дал понять, почему это было сделано.

Ответ 8

Нет конкретного использования одиночного, но вы можете рассмотреть следующую ситуацию.

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

Учтите, что someMethod() выполняет некоторую операцию, которая будет изменять переменные экземпляра или делать что-то, что повлияет на поведение позже при обработке.

Итак, в этом случае, если вы используете оператор && и первое условие не выполняется, оно никогда не войдет в someMethod(). В этом случае достаточно одного оператора &.

Ответ 9

Поскольку & - бит-мудрый оператор, вы можете выполнить до 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) {
        ...;
    }
}

См. мои другие ответы по соответствующему вопросу для получения дополнительной информации по этой теме.

Ответ 10

Единственное, что я могу придумать, - это когда вам нужно вызвать метод или выполнить код, независимо от того, будет ли первое выражение оценено как true или false:

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

// ...

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

Хотя вы можете сделать это без &:

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

Ответ 11

Короткое замыкание может привести к ошибкам в прогнозировании ветвлений на современных процессорах и значительно снизить производительность (примечательным примером является высоко оптимизированный луч с кодом пересечения оси с прямоугольным полем в трассировке лучей) [пояснение необходимо].