Java Logical Operator Short-Circuiting

Java logical operator short-circuiting

The && and || operators "short-circuit", meaning they don't evaluate the right-hand side if it isn't necessary.

The & and | operators, when used as logical operators, always evaluate both sides.

There is only one case of short-circuiting for each operator, and they are:

  • false && ... - it is not necessary to know what the right-hand side is because the result can only be false regardless of the value there
  • true || ... - it is not necessary to know what the right-hand side is because the result can only be true regardless of the value there

Let's compare the behaviour in a simple example:

public boolean longerThan(String input, int length) {
return input != null && input.length() > length;
}

public boolean longerThan(String input, int length) {
return input != null & input.length() > length;
}

The 2nd version uses the non-short-circuiting operator & and will throw a NullPointerException if input is null, but the 1st version will return false without an exception.

Java logical operator (&&, ||) short-circuit mechanism

It's simply because

if (false && true || true)

is equivalent to (&& has a higher precedence)

if ((false && true) || true)

which is

if (false || true)

which is... true.

Note: In the expression true || true && false, the part true && false is called a dead code because it doesn't affect the final result of the evaluation, since true || anything is always true.


It is worth mentioning that there exist & and | operators that can be applied to booleans, they are much like && and || except that they don't short circuit, meaning that if you have the following expression:

if (someMethod() & anotherMethod())

and someMethod returns false, anotherMethod will still be reached! But the if won't be executed because the final result will be evaluated to false.

Logical operator OR without short circuit

One example arises if we have two functions and we want them both to be executed:

if (foo() | bar())

If we had used ||, then bar() will not be executed if foo() returns true, which may not be what we want.

(Although this is somewhat of an obscure case, and more often than not || is the more appropriate choice.)

An analogous situation can come up with &/&&:

if (foo() & bar())

If we had used &&, then bar() will not be executed if foo() returns false, which again may not be what we want.

Short circuit vs non short circuit operators

One reason you might want to use the non-short-circuiting operator is if you are somehow depending on side-effects of functions. For example.

boolean isBig(String text) {
System.out.println(text);
return text.length() > 10;
}

...
if( isBig(string1) || isBig(string2) ){
...
}

If you don't care about whether the println is executed then you should use the short circuit operations as above. However, if you want both strings to be printed always (thus depending on side effects) then you need to use the non-short-circuit operator.

Practically speaking, you almost always want to use the short-circuit operators. Relying on side effects in expressions is usually bad programming practice.

One exception is in very low level or performance-sensitive code. The short-circuiting operators can be slightly slower because they cause branching in the program execution. Also using bitwise operators allows you to do 32 or 64 parallel boolean operations as a single integer operation, which is very fast.

Mixing short-circuit operators with other operators

The short-circuiting logical operators don't even evaluate the right side when short-circuiting. This is covered by the JLS, Section 15.24, which covers the || operator.

At run time, the left-hand operand expression is evaluated first; if the result has type Boolean, it is subjected to unboxing conversion (§5.1.8).

If the resulting value is true, the value of the conditional-or expression is true and the right-hand operand expression is not evaluated.

(bold emphasis mine)

So, the ++y is never evaluated, and y remains 0.

The same short-circuiting behavior exists for the && operator when the left side evaluates to false.

How Java Logical short circuit Operators Work

You have two operators. The AND operator is evaluated first and returns false. Then the OR operator is evaluated and returns true, since the second operand of the OR operator is true:

if (line.length() > 0 && !line.startsWith("/*") || !line.startsWith("--"))
false && not evaluated
false || true
true

If you want the second operand of the AND operator to include the OR operator, you should add parentheses:

if (line.length() > 0 && (!line.startsWith("/*") || !line.startsWith("--")))
false not evaluated
false

What is short circuiting and how is it used when programming in Java?

Short-circuiting is where an expression is stopped being evaluated as soon as its outcome is determined. So for instance:

if (a == b || c == d || e == f) {
// Do something
}

If a == b is true, then c == d and e == f are never evaluated at all, because the expression's outcome has already been determined. if a == b is false, then c == d is evaluated; if it's true, then e == f is never evaluated. This may not seem to make any difference, but consider:

if (foo() || bar() || baz()) {
// Do something
}

If foo() returns true, then bar and baz are never called, because the expression's outcome has already been determined. So if bar or baz has some other effect than just returning something (a side effect), those effects never occur.

One great example of short-circuiting relates to object references:

if (a != null && a.getFoo() != 42) {
// Do something
}

a.getFoo() would normally throw a NullPointerException if a were null, but because the expression short-circuits, if a != null is false, the a.getFoo() part never happens, so we don't get an exception.

Note that not all expressions are short-circuited. The || and && operators are short-circuited, but | and & are not, nor are * or /; in fact most operators are not.

Java short circuit evaluation

Advanced debugging lesson #1:

If you run into a seemingly impossible error (e.g. one that contradicts you knowledge about Java), do the following:

  • Consult a reputable text book (or better still, the relevant standard) to confirm that your understanding is not flawed. (In this case your understanding was correct, and any half-decent textbook would confirm this in a minute.)

  • Check all of the stupid things that you could have done that could cause the impossible error. Things like not saving a file, not doing a complete build, running an old / stale version of the application, being in the wrong directory, and so on.

In summary, learn to doubt yourself a bit more.

Short circuit logical operators over Iterable

Use Stream.allMatch which is a short-circuiting operation.

List<Boolean> bList = new ArrayList<>();
boolean result = bList.stream().allMatch(b -> b);


Related Topics



Leave a reply



Submit