Java Utilities

Comparing Strings

Some java programmers forget that a text is stored in different areas and that this affects the use of direct comparisons. This page attempts to show why equals and equalsIgnoreCase methods from the String class should always be used.

A java text segment can be stored as a literal in a "string pool" or as data inside a String object.

Text in String Pool


String a = "abc"; // text stored in string pool
String b = "abc"; // ref to text already in string pool
(a == b): true

These literal values can be compared directly because they will hold the same pointer.

Text in Heap String Object

However, text used to initialise a new String object cannot be compared in this way as each object will hold its own text fragment. These objects are stored directly in the heap. So, comparing String object with String Pool text does not work directly:


String c = new("abc"); // c String object stored on heap with own pointer
(c == b): false

Using the equals and equalsIgnoreCase methods from the String class does work:


(c.equals(b)): true

String objects will also have different addresses on the heap so also cannot be compared directly.


String d = new("abc"); // d String object stored on heap again with own pointer
(c == d): false
(c.equals(d)): true

Pointing to the same String Object on the Heap

Two String pointers can point to the same String object on the heap and the direct comparison will work.


String e = d); //  e String object stored on heap  with d pointer
(d == e): true
(d.equals(e)): true

Summary

The equals and equalsIgnoreCase methods from String class should always be used and is good programming practice. Direct comparison of texts may introduce hard-to-find bugs.

A source of exceptions interrupting program flow is from null pointers. Eg, this fragment is better in case d holds a null pointer:


"abc".equals(d): true, 

Code listing:

The StringComparisonTest was used to test these ideas.


public class StringComparisonTest{

  public static void main(String [] args){
    String a = "abc"; // text stored in string pool
    String b = "abc"; // text already stored in string pool
                      // so just get the reference
    System.out.println("String a = \"abc\"; // text stored in string pool");
    System.out.println("String b = \"abc\"; // ref to text already in string pool");
    System.out.println("(a == b): " + (a == b));// will be true
    
    String c = new String("abc");// c String object stored on heap with own pointer
    System.out.println("\nString c = new(\"abc\"); // c String object stored on heap with own pointer"); 
    System.out.println("(c == b): " + (c == b));// will be false  
    System.out.println("(c.equals(b)): " + (c.equals(b)));// will be true  
    
    String d = new String("abc");// d String object stored on heap again with own pointer
    System.out.println("\nString d = new(\"abc\"); // d String object stored on heap again with own pointer"); 
    System.out.println("(c == d): " + (c == d));// will be false  
    System.out.println("(c.equals(d)): " + (c.equals(d)));// will be true 

    String e = d;// e String object stored on heap  with d pointer
    System.out.println("\nString e = d); //  e String object stored on heap with d pointer"); 
    System.out.println("(d == e): " + (d == e));// will be true as both d and e point to same String object on the heap
    System.out.println("(d.equals(e)): " + (d.equals(e)));// will be true 

    System.out.println("\nequals method from String class better if not sure where text is stored.");
    System.out.println("\"abc\".equals(d): " + ("abc".equals(d)) + ", is better in case d == null" ); 
  }  
}

This baeldung site expresses many of the above ideas more clearly.