To Be or Not to Be — That Is the Question
In the world of programming, particularly in Java, the question may not be as dramatic as Shakespeare’s, but it’s certainly just as essential: “What condition must be true for the code to execute?” Logical operators are the answer. They’re fundamental building blocks of any program, helping us control the flow of execution and determine outcomes based on conditions.
In programming, we use logical operators everywhere. They are, in essence, the building blocks of programming. In this post, I will highlight the basics of the following logical operators — &&, ||, &, |
and the difference between &&
and &
, also ||
and |
.
The logical operators seem to be simple. In fact, they are, they are simple and easy to use. But there are some subtle difference that may affect the program’s performance or behaviour.
The codebase we will discuss.
import java.util.List;
import java.util.Random;
public class LogicalOperatorsDemo {
static List<String> colors = List.of("Red", "Blue", "Green", "Yellow", "Purple", "Orange", "Pink", "Brown", "Black", "White");
static List<String> darkColors = List.of("Blue", "Brown", "Black");
static List<String> favColors = List.of("White", "Blue", "Black", "Red");
static Random random = new Random();
static String color() {
return colors.get(random.nextInt(colors.size()));
}
static Boolean isDark(String color) {
System.out.println("Checking if color [" + color + "] is dark");
return darkColors.contains(color);
}
static Boolean isInMyFavColor(String color) {
System.out.println("Checking if color [" + color + "] is in myFavColors");
return favColors.contains(color);
}
}
Java Logical Operators: Basics
Before diving into tricks, let’s go through basic Java logical operators:
- AND (
&&
)
Usage: Evaluates whether both conditions are true. If both are true, the entire expression evaluates to true.
Short-circuiting: If the first condition is false, Java does not evaluate the second condition because the overall result will be false regardless.
Example:
public static void main(String[] args) {
var color = color();
// SHORT CIRCUIT
if (isInMyFavColor(color) && isDark(color)) {
System.out.println("Color [" + color + "] is both dark and in my favColors ");
}
}
Output:
Checking if color [Red] is in myFavColors
Checking if color [Red] is dark
2. OR (||
)
Usage: Evaluates whether at least one expression is true. If at least one expression is true, the result is true.
Short Circuiting: If at least one expression is true, other expressions will not be evaluated since the result will be true anyway.
Example:
public static void main(String[] args) {
var color = color();
if (isInMyFavColor(color) || isDark(color)) {
System.out.println("Color [" + color + "] is both dark and in my favColors ");
}
}
Output:
Checking if color [Black] is in myFavColors
Color [Black] is both dark and in my favColors
3. Bitwise AND (&
)
Usage: Similar to &&
but operates at the bitwise level. Returns true
if all the expressions are true.
Short Circuiting: No short-circuting. Even if the first expression returns false, the remaining expressions are not skipped, they are all evaluated. It may have performance implications if conditions are long-running ones.
Example:
public static void main(String[] args) {
var color = color();
if (isInMyFavColor(color) & isDark(color)) {
System.out.println("Color [" + color + "] is both dark and in my favColors ");
}
}
Output:
Checking if color [Brown] is in myFavColors
Checking if color [Brown] is dark // Evaluated too
4. Bitwise OR (|
)
Usage: Similar to ||
but operates at the bitwise level. Returns true if at least one condition is true.
Short Circuiting: No short-circuting. Even if the first expression evaluates to true, the remaining conditions are all evaluated.
Example:
public static void main(String[] args) {
var color = color();
if (isInMyFavColor(color) | isDark(color)) {
System.out.println("Color [" + color + "] is both dark and in my favColors ");
}
}
Output:
Checking if color [Black] is in myFavColors
Checking if color [Black] is dark
Color [Black] is both dark and in my favColors
Key Differences: &&
vs &
and ||
vs |
Now, let’s look at the key differences between the logical operators that may not be immediately obvious to beginners.
- Short-circuiting and non short-circuiting
- The
&&
and||
operators are short-circuit. That means if at least one of conditions can determine the result, all other conditions are skipped. This can lead to performance optimizations, in cases when conditions are expensive to compute. - Example:
// Short circuit
var color = color()
if (isInMyFavColor(color) || isDark(color)) {
System.out.println("Color [" + color + "] is both dark and in my favColors ");
}
// Output:
// Checking if color [Black] is in myFavColors
// Color [Black] is both dark and in my favColors
// method isDark(color) is not evaluated
- On the other hand,
&
and|
don’t short-circuit. They will evaluate all the expressions or operands regardless the result of previous operands. - Example:
// Non short-circuit
var color = "Black";
if (isInMyFavColor(color) | isDark(color)) {
System.out.println("Color [" + color + "] is both dark and in my favColors ");
}
// Output:
// Checking if color [Black] is in myFavColors
// Checking if color [Black] is dark
// Color [Black] is both dark and in my favColors
// method isDark(color) is evaluated
2. Performance considerations
- In most cases, the short-circuiting behaviour of
&&
and||
make them more efficient than&
and|
, especially when the conditions require long-running or complex calculations. - However, they can be used in cases, when it is important to check every expression, maybe for debugging or for some other reasons.
3. Bitwise Operations
&
and|
come handy when working with binary data, flags, or masks. For example, in cases when checking for specific bit is required,&
is used to check if some specific bit is set.- Example
var number = 5;
// 5 in binary == 0101
if ((number & 1) != 0) {
// Checking if the least significant bit is set
System.out.println("The least significant bit is set");
}
// Output
// The least significant bit is set
When to use:
&&
and||
- Short-circuit behavior
- Simpler behaviour without applying bitwise operations
2. &
and |
- Need for evaluating all the expressions, maybe for debugging or some other reason.
- You are performing bitwise operations.
In conclusion, logical operators like &&
, ||
, &
, and |
are foundational to Java programming. While &&
and ||
offer the convenience of short-circuiting, reducing unnecessary computations, &
and |
ensure that both operands are evaluated, offering precise control for more complex or bitwise operations.
Understanding when and how to use each operator will not only help you avoid common pitfalls but also make your code more efficient and readable.
If you liked this post, you might enjoy my other posts as well! Don’t forget to hit like and subscribe to stay updated on all my latest content. See you in the next post! Happy coding!