Are You Feeling any difficulity while writing Core-Java Programs. Send those to me Here.

Monday, 24 June 2013

Diff b/w || String s = "hello" & String s = new String("hello") ||

The String Pool

Sun made an optimization that is rather confusing to many new Java programmers called the String Pool. It allows for Strings, which are one of the most used Objects to optimize themselves and save space. An important point to make is that the String Pool only applies to String literals, meaning their value was assigned, not constructed using a String constructor. Let me start off with an example:


public class Main {

       public static void main(String[] args) {
              String s1 = "abc";
              String s2 = "abc";
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
      }
}

If you run this program, you get this:


Quote
s1 = abc
s2 = abc
s1 == s2? true
s1.equals(s2)? true


Most beginners are told to only compare strings using the .equals() method, simply because it is safer for beginners, however, what is confusing about my program above is that the == operator actually said those two were the same memory location too. How is s1 == s2? I clearly defined 2 different variables! An important concept about Java that you may not know is that symbols (variable names) aren't actually the object that they are defined as. They actually hold a reference to the spot in memory where the actual object is kept. Since Strings are SO commonly used, Strings literals that are the same are given the same address so  that it saves memory and doesn't have to make another. Think about it as if there can only be one of each String in there and anything matching is assigned a reference to that String. However, once all references are gone, then the object is erased. However, once they change, the addresses are different:

public class Main {

       public static void main(String[] args) {
              String s1 = "abc";
              String s2 = "abc";
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
              s2 += "abc";
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
        }
}

This outputs this:



Quote
s1 = abc
s2 = abc
s1 == s2? true
s1.equals(s2)? true
s1 = abc
s2 = abcabc
s1 == s2? false
s1.equals(s2)? false


HOWEVER, if for any reason you want the variables to not occupy the same location in memory, there are two ways to do this. First, you can use the String constructor, since those are not put into the pool:

public class Main {
       public static void main(String[] args) {
              String s1 = "abc";
              String s2 = new String("abc");
              System.out.println("s1 = " + s1);   
              System.out.println("s2 = " + s2);       
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
       }
}

This code says that == is false. And just to prove a point, let's make both of them use constructors:


public class Main {
       public static void main(String[] args) {
              String s1 = new String("abc");
              String s2 = new String("abc");
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
       }
}

That code yields:


Quote
s1 = abc
s2 = abc
s1 == s2? false
s1.equals(s2)? true


OR you can assign them in different steps, forcing it to occupy a diffent address for each addition. (First they are the same, then s1 is moved to another spot to add on the "c" and the same for s2):

public class Main {
       public static void main(String[] args) {
              String s1 = "ab";
              String s2 = "a";
              s1 += "c";
              s2 += "bc";
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
       }
}


Quote
s1 = abc
s2 = abc
s1 == s2? false
s1.equals(s2)? true


However, there is a way for a literal and a constructor value (assuming value is the same) to == each other. cfoley introduced me to the intern() method of the String class. What this method does is it looks at its value, and if it matches a value ALREADY IN the String pool, it returns a reference to the object in the pool, else it adds itself to the pool. Observe:

public class Main {
       public static void main(String[] args) {
              String s1 = "abc";
              String s2 = new String("abc");
              s2 = s2.intern();
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
       }
}

This returns:


Quote
s1 = abc
s2 = abc
s1 == s2? true
s1.equals(s2)? true


Cool huh? This happens because s1 is ASSIGNED to "abc" and is added to the pool. s2, however, is constructed to "abc", but not added to the pool. But the intern() method sees that the VALUE "abc" is already in the pool and thus returns the reference to s1. I got a challenge problem for you now. Will s1 == s2 in the following code?

public class Main {
       public static void main(String[] args) {
              String s1 = new String("abc");
              String s2 = new String("abc");
              s2 = s2.intern();
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
       }
}

... The answer is no. The intern() method is called, but neither s1 nor s2 is in the pool so all the intern() method does is add it to the pool. To make THESE ==, you must call intern() twice so that there is already a reference in the pool (from the first call).

public class Main {
       public static void main(String[] args) {
              String s1 = new String("abc");
              String s2 = new String("abc");
              s2 = s2.intern();
              s1 = s1.intern();
              System.out.println("s1 = " + s1);
              System.out.println("s2 = " + s2);
              System.out.println("s1 == s2? " + (s1 == s2));
              System.out.println("s1.equals(s2)? " + (s1.equals(s2)));
       }
}


Quote
s1 = abc
s2 = abc
s1 == s2? true
s1.equals(s2)? true

No comments:

Post a Comment