The String Pool
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
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
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
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
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
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
s2 = abc
s1 == s2? true
s1.equals(s2)? true
No comments:
Post a Comment