Comparing strings: equals, ==, and ordering
Objects and text10 min readLesson 5 of 5
5.1 References and content
A variable of type String holds a reference to an object. A String is immutable, so its characters never change after it is created. The == operator compares references, so it asks whether two variables point to the same object. It does not read the characters. Two strings can hold identical text and still be separate objects.
equals compares content. answer.equals("yes") returns true when both strings contain the same characters in the same order. equalsIgnoreCase does the same while ignoring letter case.
For text that comes from input, a file, or a network response, equals is the safe choice. The == operator belongs in tests of object identity, such as checking whether two references point to one shared instance.
public class Main {
public static void main(String[] args) {
String language = "Ja";
String built = language + "va";
String literal = "Java";
System.out.println(built == literal);
System.out.println(built.equals(literal));
System.out.println(built.equalsIgnoreCase("JAVA"));
}
}Output
false true true
5.1.1 Literals and interning
String literals with the same characters are reused from a pool. In String a = "java"; String b = "java";, both variables usually point to the same pooled object, so a == b is true. A string built at runtime with concatenation or new String(...) is a separate object, so == is false even when the text matches.
intern() returns the pooled string for a given content. It can make a reference comparison true, but the method is rarely needed in application code. Using equals avoids depending on the pool.
Do not learn the rule from a few examples in a small program. The result depends on how each string was created, which makes == an unreliable habit for text.
public class Main {
public static void main(String[] args) {
String a = "java";
String b = "java";
String c = new String("java");
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a == c.intern());
System.out.println(a.equals(c));
}
}Output
true false true true
5.1.2 Ordering text
compareTo orders strings by their characters. It returns a negative number when the left string sorts before the right, zero when they are equal, and a positive number when it sorts after. The exact number is an implementation detail, so compare the result with 0. The exact negative or positive value is not part of the contract.
compareToIgnoreCase applies the same ordering without case. Sorting a list of names usually uses one of these methods through a comparator, which the collections module covers.
Uppercase letters sort before lowercase letters in the default order because their code points are lower. That can put "Zebra" before "apple". For a display list, decide whether case should matter before choosing the comparison method.
public class Main {
public static void main(String[] args) {
String a = "apple";
String b = "banana";
System.out.println(a.compareTo(b) < 0);
System.out.println(a.compareTo("apple"));
System.out.println(b.compareTo(a) > 0);
System.out.println(a.compareToIgnoreCase("Apple"));
}
}Output
true 0 true 0
5.1.3 Null safety
Calling a method on a null reference throws NullPointerException. In answer.equals("yes"), a null answer fails before the comparison happens. Put the literal first, as in "yes".equals(answer), because a literal is never null.
Objects.equals(left, right) handles null on either side and returns true when both are null. It is a good fit when both values may be missing. Objects.requireNonNull(value, "message") is for places where null is a programming error and a clear failure early is better.
Validation belongs near the input. Check for null or blank text before the value reaches a comparison, then the rest of the method can use it without repeated guards.
import java.util.Objects;
public class Main {
public static void main(String[] args) {
String answer = null;
System.out.println("yes".equals(answer));
System.out.println(Objects.equals(answer, "yes"));
System.out.println(Objects.equals(null, null));
}
}Output
false false true
5.1.4 Comparing in conditions
An if or a switch on a string compares content. The runtime uses equals for switch labels, so a switch on "START" matches only that exact spelling. Use equalsIgnoreCase first when the input may arrive in mixed case.
For a menu, normalize the input once: String choice = input.strip().toLowerCase(); then compare against lowercase labels. This keeps the conditions short and the accepted spellings in one place.
Objects.equals also works in a condition. Pick one style for a method and stay with it, so a reader does not have to check which comparison rule applies at each line.
public class Main {
public static void main(String[] args) {
String command = "START";
switch (command) {
case "START":
System.out.println("Starting");
break;
case "STOP":
System.out.println("Stopping");
break;
default:
System.out.println("Unknown");
}
System.out.println(command.equalsIgnoreCase("start"));
}
}Output
Starting true
5.1.5 Practice
- Create two strings with the same text, one literal and one built at runtime. Print the results of
==,equals, andequalsIgnoreCase. - Compare
"apple"with"Banana"usingcompareToand print whether the result is negative. - Make a null string and compare it safely with a literal and with
Objects.equals. - Normalize a mixed-case command and switch on it.
- Compare three words pairwise and print which one sorts first.
For step 3, the program should print a result even when the value is null, so keep the literal on the left in the first comparison.
