Relational Operators in Java
Boolean operators form complicated conditions from boolean values.
Why Single Comparisons Are Not Enough
Programs make decisions by asking questions that have a true or false answer. Is the score high enough to pass? Is the user name empty? Is the price below the budget? In English we join these questions with the words and, or, and not. Java provides boolean operators for the same purpose. They combine or change boolean values so that a program can make a decision using more than one condition.
This article builds that toolkit in four parts: the relational operators that produce a boolean value from two values, the logical operators && and || that combine boolean values, the ! operator that reverses one, and the special cases where a comparison that looks correct does not behave the way you expect.
What do you think happens?
What does the following line print? System.out.println('Z' < 'a');
Reveal answer
true
A char compares through its numeric Unicode value. The character 'Z' has a smaller numeric code than 'a', because all uppercase letters come before all lowercase letters. So 'Z' < 'a' is true, even though Z comes after a in the alphabet.
The Six Relational Operators
A relational operator compares two values and produces a boolean value, either true or false. Java's relational operators are ==, !=, <, >, <=, and >=.
| Operator | Tests whether | Example | Result |
|---|---|---|---|
| < | the left value is less than the right value | 5 < 7 | true |
| <= | the left value is less than or equal to the right value | 7 <= 7 | true |
| > | the left value is greater than the right value | 5 > 7 | false |
| >= | the left value is greater than or equal to the right value | 5 >= 7 | false |
| == | the two values are equal | 5 == 7 | false |
| != | the two values are not equal | 5 != 7 | true |
The six relational operators in Java, applied to the numbers 5 and 7
The operators are read from left to right. In each row, the left operand is compared with the right operand. Notice that == tests equality, not assignment. A single equals sign stores a value in a variable, while a double equals sign asks whether two values match. Relational operators can compare values of any numeric type, and they can also compare char values, which the next section covers.
Comparing Numbers and Characters
Relational operators work on numeric values of any numeric type. The following program applies all six operators to the same pair of int values. Before reading the output, try to predict each line.
public class NumericComparisons {
public static void main(String[] args) {
int a = 5;
int b = 7;
System.out.println(a < b);
System.out.println(a <= b);
System.out.println(a > b);
System.out.println(a >= b);
System.out.println(a == b);
System.out.println(a != b);
}
}- 1a < b asks whether 5 is less than 7. It is, so the result is true.
- 2a <= b asks whether 5 is less than or equal to 7. The result is true.
- 3a > b asks whether 5 is greater than 7. It is not, so the result is false.
- 4a >= b asks whether 5 is greater than or equal to 7. The result is false.
- 5a == b asks whether 5 equals 7. The result is false.
- 6a != b asks whether 5 differs from 7. The result is true.
- a and b hold the values 5 and 7
- Each println prints the boolean result of one comparison
true
true
false
false
false
trueA char value is compared through its numeric Unicode value, not through its position in the alphabet. This is why the ordering of characters can surprise a beginner. All uppercase letters come before all lowercase letters, so 'Z' is less than 'a', and 'A' is less than 'a'. Comparisons between characters that share the same case do follow alphabetical order, because their numeric codes increase through the alphabet.
true
true
trueCombining Conditions with && and ||
The operator && (and) produces true only when both operands are true. The operator || (or) produces true when at least one operand is true. Each operand is itself a boolean value, often the result of a relational comparison. Because the operators combine boolean values, a single condition can test several facts at once.
| a | b | a && b | a || b | !a |
|---|---|---|---|---|
| true | true | true | true | false |
| true | false | false | true | false |
| false | true | false | true | true |
| false | false | false | false | true |
Truth table for the operators &&, ||, and ! over every combination of two boolean inputs
Deciding whether a value is in a range
A program should accept a temperature t only if it is at least 0 and less than 100. Write a condition that is true exactly when t is in that range, and decide the result for t = 25 and t = 100.
- 1Split the requirement into two tests
The first test is t >= 0. The second test is t < 100. Both must hold, so the two tests are joined with &&.
- 2Write the combined condition
The condition is t >= 0 && t < 100. Each side is a relational comparison that produces a boolean value, and && combines them.
- 3Evaluate for t = 25
25 >= 0 is true. 25 < 100 is true. Both operands are true, so the complete result is true.
- 4Evaluate for t = 100
100 >= 0 is true. 100 < 100 is false. One operand is false, so && produces false. The value 100 is outside the range.
The condition t >= 0 && t < 100 is true for 25 and false for 100.
How Short-Circuit Evaluation Skips Work
Java evaluates && and || with short-circuit evaluation. Java reads the expression from left to right and stops as soon as the final result is known. For &&, once the left operand is false, the whole expression must be false, so Java does not evaluate the right operand. For ||, once the left operand is true, the whole expression must be true, so the right operand is again skipped. The second operand may therefore not be evaluated at all.
This behavior is useful as a guard. The source's example reads the expression from left to right: the first operand asks whether x is not zero, and the second asks whether y divided by x is greater than 1. Because the first operand protects the division, the division happens only when x is not zero. If Java evaluated both operands unconditionally, a zero value of x would cause a divide-by-zero error with integer values.
public class ShortCircuitGuard {
public static void main(String[] args) {
int x = 0;
int y = 10;
if (x != 0 && y / x > 1) {
System.out.println("Condition is true");
} else {
System.out.println("Condition is false");
}
}
}- 1x != 0 is evaluated first. Because x is 0, the result is false.
- 2Since the left operand of && is false, the whole condition is false.
- 3Java skips y / x > 1 entirely, so no division by zero occurs and no exception is thrown.
- 4The else branch runs and prints Condition is false.
- x is 0, so the first operand is false
- The division y / x is never evaluated
Condition is falseReversing a Boolean Value with !
The ! operator reverses the value of a boolean. A true value becomes false, and a false value becomes true. Unlike && and ||, the ! operator does not combine two boolean values. It changes one boolean value. This makes it useful for asking the opposite question, such as whether a condition does not hold.
true
true
true- !done reverses the value false to true
- !(5 > 7) reverses false to true
- 5 > 3 && 2 > 4 is false, so ! turns it into true
Negating a compound condition changes each result. Because ! applies to the whole value it is given, the parentheses matter. The expression !(5 > 3 && 2 > 4) reverses the result of the entire && comparison, not just one side of it.
Finding the Cause of an Unexpected Result
When a compound condition produces an unexpected value, evaluate it in pieces. Find the grouping first: parentheses show which sub-expression is computed first, and the usual precedence of && over || decides the rest. Then compute each leaf comparison, then each combination, working toward the final result. The sub-result that determines the final value is the place to look.
Trace the tree from the bottom up. a > b is true, and c != d is false. Combining them with && gives false, so the left side of || is false. The right side, !e, is true because e is false. The || then has a true operand, so the final result is true. The true result comes from !e, not from the && part, and that is the sub-result to inspect when the outcome surprises you.
Comparing Boolean Values with == and !=
The operators == and != can compare boolean values, including the results of other comparisons. This allows a program to check whether two conditions agree with each other. The comparison is itself a boolean expression, so its result can be combined with other operators.
true
false- 5 > 3 is true, so it matches p
- p != true is false because the two values are equal
Comparing String Contents Correctly
String contents should be compared with equals() or compareTo(). The == operator does not compare the characters inside two strings. It checks object storage location, meaning whether two variables refer to the same String object. Two different String objects can contain identical text and still fail an == test.
public class StringComparison {
public static void main(String[] args) {
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
System.out.println(s1.compareTo(s2));
}
}- 1s1 == s2 checks whether s1 and s2 refer to the same object. They do not, so the result is false.
- 2
s1.equals(s2)checks whether the contents match. Both contain Java, so the result is true. - 3
s1.compareTo(s2)compares the contents and returns 0, which indicates that the two strings are equal.
- s1 and s2 refer to two different String objects with the same characters
compareToreturns 0 when the contents are equal
false
true
0Choose equals() when you need a yes or no answer about whether two strings have the same contents. Choose compareTo() when you also need the ordering of two strings. Use relational operators such as < and > for numeric values and char values, not for String contents.
Testing for Double.NaN
Double.NaN is a special floating-point value that stands for not a number. The source identifies Double.NaN as one of the two cases that require special care. A relational comparison with it does not behave like a comparison with an ordinary number, so x == Double.NaN cannot be used to detect a NaN value. The reliable test is Double.isNaN(x), which returns true when x holds NaN.
public class NaNCheck {
public static void main(String[] args) {
double x = Double.NaN;
System.out.println(x == Double.NaN);
System.out.println(Double.isNaN(x));
}
}- 1x ==
Double.NaNreturns false, even though x is NaN. A NaN value is not equal to NaN in a relational comparison. - 2
Double.isNaN(x)examines x and returns true, which is the correct answer for this test.
- x holds NaN
- The relational operator gives the wrong answer for this test
Double.isNaNgives the correct answer
false
trueMistakes Beginners Make with Comparisons
Common mistake Comparing String contents with ==
What it looks likeif (name == "Ada") where name was read from user inputWhy it fails== checks whether two variables refer to the same object, not whether their characters match.
Do this insteadUse name.equals("Ada") to compare the contents.Common mistake Using == to test for NaN
What it looks likeif (x == Double.NaN)Why it failsThe comparison returns false even when x holds NaN, so the test never detects the value.
Do this insteadUse Double.isNaN(x).Common mistake Placing the risky operation before its guard
What it looks likeif (y / x > 1 && x != 0)Why it failsThe left operand is evaluated first, so the division runs even when x is 0.
Do this insteadWrite x != 0 && y / x > 1 so the guard is evaluated first and short-circuit evaluation skips the division.
Common mistake Assuming char ordering is alphabetical across cases
What it looks like'Z' < 'a' is expected to be falseWhy it failsA char compares through its numeric Unicode value, and all uppercase letters come before all lowercase letters.
Do this insteadPredict with the numeric codes, or compare characters of the same case when alphabetical order is needed.
Common mistake Forgetting that ! applies to the whole value it is given, so parentheses are needed when negating a compound condition
Practice: Predict Each Result
Without running the code, predict the output of the following program, then check your answer. Write down the result of each comparison first, and then decide whether the if statement prints yes or no. int x = 0; int y = 8; boolean flag = false; System.out.println(x != 0 && y / x > 2); System.out.println(!flag || y > 10); System.out.println(Double.isNaN(0.0 / 0.0));
Hints
- For the first line, the left operand x != 0 is false, so check whether the right operand is evaluated.
- For the second line, !flag is true, and || needs only one true operand.
- The expression 0.0 / 0.0 produces NaN in Java floating-point arithmetic.
A program should print yes when a student's name equals Ada, ignoring nothing else, and the score is between 60 and 100 inclusive. Write a single if condition using equals(), relational operators, and &&. Put the test that cannot cause an error first where a guard is needed.
Hints
- Use
name.equals("Ada")for the string test, not ==. - Between 60 and 100 inclusive means score >= 60 and score <= 100.
Key Takeaways
- Java's six relational operators, ==, !=, <, >, <=, and >=, compare values and produce a boolean result, and they work on numeric and char values, where char values compare through their Unicode numeric codes.
- && is true only when both operands are true, || is true when at least one is true, and ! reverses a single boolean value.
- Short-circuit evaluation stops && and || as soon as the result is known, so the second operand may be skipped; this lets a guard protect a risky operation such as division by zero.
- Use
equals()orcompareTo()to compare String contents, because == checks object storage location, and useDouble.isNaN(x)to test for NaN, because a relational comparison withDouble.NaNdoes not detect it. - When a compound condition gives an unexpected result, evaluate its sub-expressions one at a time, from the innermost comparisons outward, to find which sub-result determines the final value.