В каких случаях лучше использовать безусловный AND (& вместо &&)
Я хотел бы знать некоторые случаи в Java (или, более широко:
в программировании), когда в булевых выражениях предпочтительнее использовать безусловный AND
(&
) вместо условной версии (&&
).
Я знаю, как они работают, но я не могу думать о случае, когда стоит использовать один &
.
Ответы
Ответ 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
Короткое замыкание может привести к ошибкам в прогнозировании ветвлений на современных процессорах и значительно снизить производительность (примечательным примером является высоко оптимизированный луч с кодом пересечения оси с прямоугольным полем в трассировке лучей) [пояснение необходимо].