Sunday, 28 April 2013

intern() method of String class in Java

There are two ways to create String. These are:
1. Using String literals
2. Using new keyword

But what is the difference between these two approaches. Lets take an example:

String test1 = "Ramesh";
String test2 = "Ramesh";
String test3 = new String("Ramesh");
System.out.println("test1 == test2 : "+ (test1 == test2)); // true
System.out.println("test1 == test3 : "+ (test1 == test3)); // false

If we see test1 and test2 results to true and test1 and test3 results to false on comparing with == operator.

Reason: As I discussed in my previous post Mystery of equals() and == in Java, == operator always checks for the reference equality i.e if two references are pointing to the same object the result of comparison will be true.

Here test1 and test2 are pointing to the same Object. How?? The answer is - The creation of two strings with the same sequence of letters without the use of the new keyword will create pointers to the same String in the Java String literal pool.

Now what is String Pool??

String allocation, like all object allocation, proves costly in both time and memory. The JVM performs some trickery while instantiating string literals to increase performance and decrease memory overhead. To cut down the number of String objects created in the JVM, the String class keeps a pool of strings. Each time your code create a string literal, the JVM checks the string literal pool first. If the string already exists in the pool, a reference to the pooled instance returns. If the string does not exist in the pool, a new String object instantiates, then is placed in the pool. Java can make this optimization since strings are immutable and can be shared without fear of data corruption.
intern method() - A JVM has a string pool where it keeps at most one object of any String. String literals always refer to an object in the string pool. String objects created with the new operator do not refer to objects in the string pool but can be made to using String's intern() method. Thejava.lang.String.intern() returns an interned String, that is, one that has an entry in the global String pool. If the String is not already in the global String pool, then it will be added. In continuation to the previous example,do the following changes and you will see that test1 and test3 are pointing to the same object.


System.out.println("test1 == test3 : "+ (test1 == test3.intern())); // true

Mystery of equals() and == in Java

The very first question which comes to my mind is - What exactly the difference between equals() and ==. Below is the small code which can answer this question

public boolean equals(Object o) - this method is declared in Object class and == is an operator. == and equals() always check for reference equality i.e it will compare whether two references are equal or not.  But in case of String and Wrapper classes equals will always check for object value equality. Lets have a look on an example:

public class TestEqual
{
 String name;
 TestEqual(String name)
 {
  this.name = name;
 }
 public static void main(String args[])
 {
  TestEqual t1 = new TestEqual("Ankit");
  TestEqual t2 = new TestEqual("Ankit");
  System.out.println("t1==t2 results to" + (t1 == t2));
  System.out.println("t1.equals(t2)results to" + (t1.equals(t2)));
 }
}

In both the cases output will be false. If you try to print t1.hashcode() and t2.hashcode() alone you will get two different hashcodes.
But what if I want to change this behavior. The answer is, I need to override equals() method which happens in String and all the wrapper classes. Lets have a look on the example

public static void main(String args[])
{
 String t1 = “Ankit”;
 String t2 = "Ankit";
 System.out.println("t1==t2 results to"+(t1==t2));
 System.out.println("t1.equals(t2)results to"+(t1.equals(t2)));
 System.out.println(t1.hashCode() +"," +t2.hashCode() );
}

In both the cases output will be true. If you try to print t1.hashcode() and t2.hashcode() alone you will get same hashcodes.
Lets override equals() method for our class TestEqual which I have mentioned above:

public boolean equals(Object test)
{
 if (this.name == ((TestEqual)test).name)
 {
  return true;
 }
 else
  return false;
 }
}

Now try printing the output for these two statements

System.out.println("t1==t2 results to"+(t1==t2));   // This will give you false
System.out.println("t1.equals(t2)results to"+(t1.equals(t2))); // This will give you true

You must be thinking that’s all we have to do and our code will work fine. But still we are missing a very important concept. Any guesses – we forgot to override hashcode() which is also declared in the Object class.
But what is the need behind overriding the hashcode() method. Overriding the equals method has already solved my problem. Still we can't ignore the contract between equals() and hashcode() methdod which states that –
If equals says that two object references are equal then their hashcode will always be equal but if equals says that two object references are NOT equal then their hashcode may or may not be equal. Again lets have a look on an example which will justify the need of overriding hashcode method:

TestEqual t1 = new TestEqual("Ankit");
TestEqual t2 = new TestEqual("Ankit");
Set<TestEqual> strset = new HashSet<TestEqual>();
strset.add(t1);
strset.add(t2);
System.out.println("strset.contains check: "
+ strset.contains(new TestEqual("Ankit")));
public boolean equals(Object test)
{
 if (this.name == ((TestEqual)test).name)
 {
  return true;
 }
 else
  return false;
 }
}

The output will be strset.contains check: false in this case. Oops but we override the equals() method. But that doesn’t hold good in this case. The reason is hashcode of t1, t2 and new TestEqual() object are still different and in order to make them equal, hashcode() method should be overridden. Once you override hashcode method you will get the desired output i.e strset.contains check: true

Public int hashCode()
{
   return 1;
}

Conclusion: equals and == always check for reference equality. In order to make equals() to check for Object value equality we need to override equals() and hashcode() method which happens in String and all the wrapper classes. J