Tuesday, May 18, 2010

Strange String Comparisons Behavior in Java and the Literal String Pool


Got the following question from a student in my Introduction to Programming class.

I tried to compare two String variables with the == operator, and it wasn't working (have to use .equals()). I tried again right now, and it is working. I don't understand.

I suspect he ran into String Literal comparison quirk. Here is an example to demonstrate the point.

1    public class StringCompareTest 
2    { 
3       public static void main(String args[]) 
4       { 
5          String a = "123"; 

6          String b = "123"; 
7     
8          // 1) Comparing Strings in the literal String pool 
9          if(a == b) 
10            System.out.println("a equal b"); 

11         else 
12            System.out.println("a does not equal b"); 
13    
14         String c = new String("123"); 

15         // 2) Comparing String from literal String pool 
16         //    to a new String Object (not in the pool) 
17         if(a == c) 
18            System.out.println("a equal c"); 

19         else 
20            System.out.println("a does not equal c"); 
21    
22         // 3) Finally, what you should always use when 
23         //    comparing Objects, the equals method 

24         if(a.equals(c)) 
25            System.out.println("a equal c"); 
26         else 
27            System.out.println("a does not equal c"); 

28    
29      } 
30   } 
31   

In Java, String literals go into a memory pool call the Literal String Pool. The reason this is done is to save memory. If two different Strings are initialize to the same literal value (“123”). Java assigns the same reference to a String Object from the literal String Pool. In the above example, both a and b are assigned the same literal value of “123”. Rather than creating two String literal Objects, Java creates one and puts it in the literal String pool. Anytime it is used again, it can be reused from the literal String Pool. This can be misleading to new Java developers because the standard procedure is to tell students to use the equals() method to compare Objects for equality and only use == for primitives. In the case where the literal String already exists in the literal String pool, using == (shallow comparison) works because new Strings being assigned the same literal value will simply get the reference to a String from the literal String pool.

To further illustrate the difference, if a new String Object is created using the new operator and initialized in the constructor to the String “123”, then technically, both Strings a and c are equal. However, when we use == for the comparison, String a does not equal c. String c was initialized in the constructor to the literal value “123”. However, because String c was explicitly created using the new operator and therefore not part of the literal String pool, String comparison using == (shallow comparison) does not work.

In the end, step 3, we return to doing Object comparisons the right way. The last test use the equals() method to perform a deep comparison of the two String Objects (a and c). The output confirms that both Strings are equal. Moral of the story, stick with equals method for comparing Objects.