Strings in Java are objects representing a sequence of characters. They can be compared either based on their content, or based on reference (the memory address at which the string is stored).

Comparing strings by content with .equals()

We usually want to compare strings on the basis of content rather than reference. Even though two strings may be stored in different memory locations, they may have the same content. For such content comparisons, we can use the .equals() method on the String object. An example is shown below:

String s1 = "hello";
String s2 = "world";
String s3 = "Hello";
String s4 = "hello";
// dynamically create the string "hello" at runtime
String s5 = String.format("he%s", "llo");

System.out.println(s1.equals(s2)); // prints false
System.out.println(s1.equals(s3)); // prints false
System.out.println(s1.equals(s4)); // prints true
System.out.println(s1.equals(s5)); // prints true
  • s1 and s2 have the content hello and world, as a result s1.equals(s2) returns false.
  • s1 and s3 vary by one character (lowercase and uppercase H), so s1.equals(s3) returns false.
  • s1, s4 and s5 have the same content, so both expressions s1.equals(s4) and s1.equals(s5) returns true.

Comparing strings case-insensitively with .equalsIgnoreCase()

The .equalsIgnoreCase() method on the String object is similar to equals, however, it does not consider the case of the characters. An example is shown below:

String s1 = "hello";
String s2 = "world";
String s3 = "heLlo";

System.out.println(s1.equalsIgnoreCase(s2)); // prints false
System.out.println(s1.equalsIgnoreCase(s3)); // prints true
  • s1 and s2 do not have the same content even after disregarding case, so s1.equals(s2) returns false.
  • s1 and s3 only differ in their case. As .equalsIgnoreCase() does not consider this difference, it returns true.

Comparing strings by reference with ==

To compare two strings by reference (the memory location that they're stored in) we can use the == operator. An example is shown below:

String s1 = "hello";
String s2 = "world";
String s3 = s1;
String s4 = "hello";
// dynamically create the string "hello" at runtime
String s5 = String.format("he%s", "llo");

System.out.println(s1 == s2); // prints false
System.out.println(s1 == s3); // prints true
System.out.println(s1 == s4); // prints true
System.out.println(s1 == s5); // prints false
  • s1 and s2 are two different strings stored in different memory locations, so s1 == s2 returns false.
  • As we've assigned s3 = s1, both s1 and s3 point to the same memory location. So, s1 == s3 returns true.
  • Although s1 and s4 are initialized in different statements, the Java compiler points both s1 and s4 to hello to save memory, in a process called string interning. So, s1 == s4 returns true.
  • Finally, s1 and s5 have the same content of hello, but s5 is dynamically created at runtime. As a result, s1 and s5 are stored in different memory locations, and s1 == s5 returns false.

As we saw above, the strings s1 and s4 were interned to the same reference. However, since strings in Java are immutable, if we modify s1 or s4, it would cause a new String object to be created and s1 or s4 would point to this new object. So, changes to s1 wouldn't affect s4, and s1 == s4 would return false after modification.

String s1 = "hello";
String s4 = "hello";

System.out.println(s1 == s4); // prints true

s1 += " world";
System.out.println("s1 = " + s1); // prints s1 = hello world
System.out.println("s4 = " + s4); // prints s4 = hello

System.out.println(s1 == s4); // prints false

As mentioned above, after modifying s1, they no longer point to the same memory location and so s1 == s4 returns false.