Does Java string pool store duplicate literals? - java

As I have understood, whenever we create a String literal, the pool is checked for any existing String with the same value. If it exists, a reference to the same is returned. Otherwise a new literal is created.
From this, I understand that pool only contains non-duplicate String literals.
But I am confused by the output of the following code:
String str1 = "Hello World";
String str2 = "Hello";
String str3 = str2+" World";
System.out.println(str3);
System.out.println(((str1 == str3) ? "equal":"unequal"));`
Since str3 is evaluating to "Hello World" which already exists in the pool pointed to by str1, a reference to the same should be assigned to str3 and hence str1 and str3 should be equal.
But the code is showing them as unequal.
Would appreciate if someone can explain.

str2 + " World" is not a constant expression, so it is not interned. If str2 were final or it were directly written as "Hello" + " World", then the value would be interned.
§3.10.5. String Literals:
Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.
See also: §15.28. Constant Expressions

When you concatenate the strings, you're creating a new string which is different from the one in the pool.
If you call the method intern() then you'll get the expected behavior.
jshell> var a = "hello"
a ==> "hello"
jshell> var b = " world"
b ==> " world"
jshell> a + b == "hello world"
$3 ==> false
jshell> (a + b).intern() == "hello world"
$4 ==> true

I agree with #Unmitigated
When you are writing
String str3 = str2+" World";
You are in fact creating a string that cannot be known before runtime (or maybe with compiler optimisations, but that's not the point). So the string is considered to be unique
That's the reason why each string should be compared with equals method and never with "=="

As I have understood, whenever we create a String literal, the pool is checked for any existing String with the same value.
The key word is "literal".
String str2 = "Hello";
String str3 = str2+" World";
"Hello" is a string literal.
" World" is a string literal.
str2 + " World" is an expression that concatenates two string values.
A "literal" literally appears as itself in the source code.
It would not be an optimization if the result of every string operation needed to check the literal pool to see if that string had ever appeared before. The overhead is acceptable at compile time, since that's a one-time cost.

Related

Basic query regarding String

This is a very basic question regarding String.
String str1 = "abc";
String str2 = "abc";
System.out.println("out put " + str1 == str2);
I was shocked when I executed the program. I got false.
According to me, string literals are shared between the String references if another string wants to point to the same String literal. JVM will check it in String pool first and if it is not there then it will create one and give the reference, otherwise it will be shared between multiple String references like in this case (according to me).
So if I go by my theory then it should have been returning true as both the String reference point to same String literal.
You need to do the following to check it correctly:-
System.out.println("out put " + (str1 == str2));
This will give you true as expected.
Your statement does "out put" + str1 and then tries to equate it with str2
You're right about the String behaviour. But, you forgot about operator precedence. First addition is executed, later equality.
So, in your case, firstly "out put " + str1 is executed, which gives "out put abc". Later this is compared to str2, which gives false.
You meant "out put " + (str1 == str2), which indeed gives true.

How to programmatically prove that String s = new String("abc") adds the literal String "abc" to the String pool

I was asked this question somewhere : In
String s=new String("abc");
how to programmatically prove that the String literal "abc" will be added to the String pool?
PS: I know the JLS says - All String literals(Strings enclosed between "") are added to the String pool.
Also, I looked at the Byte code generated, but in Byte code , I can see only Class Constants pool not Strings Constant pool.
You can use
s != s.intern()
which yields true.
Explanation: Assume "abc" was not in the string pool before calling s.intern(). Then s.intern() would add s to the string pool and both sides would be equal(reference equality ==).
E.g. this prints false:
String s2 = "x".concat("yz"); // "xyz" is not in the string pool, since it's calculated using a method
System.out.println(s2 != s2.intern());
Since both sides are not equal, "abc" was in the string pool before calling intern(), which proofs the claim. (reductio ad impossibilem)
Note: This does not work, if you assign the string literal directly by using String s = "abc";. However you could still create a new string using new String(s) in that case and work with the new string.

Why am I getting different outputs with .concat() and += with Java Strings?

Studying for OCAJ7
I know String objects are immutable. I know using methods like .concat() on a String object will just create a new String object with the same reference name. I'm having a hard time, however, with wrapping my head around the following:
String str1 = "str1";
String str2 = "str2";
System.out.println( str1.concat(str2) );
System.out.println(str1);
// ouputs
// str1str2
// str1
String str3 = "fish";
str3 += "toad";
System.out.println(str3);
// outputs
// fishtoad
If strings are immutable, why does using += to concatenate affect the original String object, but .concat() does not? If I only want a String to concatenate with using +=, is using String better than using StringBuilder, or vice versa?
because you are catching the reference of newly generated String instance in str3
str3 += "toad";
is
str3 = str3 + "toad"
The concat method is more like + than +=. It returns the new string; it doesn't modify the original string or the argument (Strings are immutable).
str1.concat(str2)
is equivalent to str1 + str2, so str1 isn't modified, and the result is discarded.
However, += also assigns the result back to the left side, and str3 now refers to the new string. That's the difference.
Yes you are right Strings are immutable.
But when you write str1.concat(str2) inside System.out.println() you are printing the result returned by the concat() function.The result is a new String which is outputted on the console.
You haven't assigned the value to str1.
But when you write += you are first concatenating something to the String the then assigning the reference back to str1.
This explains the output.
What's happening here is that String.concat(String); does not actually combine str1 and str2 together, instead it returns a new String object that is equivalent to str1str2. When you use str1 += str2, you are actually combining the variables, and then putting the value into str1.
str3 += "toad" means
str3 = str3 + "toad";
So you concatenate and assign the result back to str3
concat() method creates a new string. You have to remember that Strings in Java are immutable.
str1 will point to the newly created string if the o/p is assigned to it,
str1 = str1.concat(str2);
otherwise str1 continues to point to the old string.
If you are going to concat multiple strings together in a loop, then it is better to use StringBuilder
Above you are not changing the reference of str1 in System.out.println(str1.concat(str2) ) so it is temporary storing the reference of str1.concat(str2).
But in str3+=str4 whole reference get moved to "fishtoad" therefore it get changed.But again if you defined a string str5="fish" then it reference back to previous location which was referencing by str3.

a confusion about java String literal pool and String's concatenation

all,
i faced a problem when a write the code below
String hello = "Hello";
String str5 = "Hel" + "lo";
String str8 = "Hel";
String str9 = "lo";
String str10 = str8 + str9;
System.out.println("str10==hello?" + (str10 == hello));
System.out.println("str5==hello?" + (str5 == hello));
System.out.println("str10==str5?" + (str10 == str5));
then i run my code and the console print this
str10 == hello ? false
str5 == hello ? true
str10 == str5 ? false
this confused me a lot. why the second print TRUE but the first print FALSE??
in my comprehension of String literal pool,when a string defined and JVM will check if the pool contains that string,if not ,put the string into the pool.in my code,variable hello exists in string pool,"Helo" and "lo" also in the pool,my question is
if the result of the concatenation of "Helo" and "lo" exists in the pool.
what's the difference between the definition about str5 and str10s',and why they are not "=="? does str5 and str10 refer to the diffrent "Hello” that in the string pool?("==" seems to mean the reference is the same object)
my jdk version :1.6.0_29
my IDE:Intellij Idea 11.2
anyone can point it out? thank you very much
It behaves as it should. It is adressed in two sections of the JLS.
JLS #3.10.5:
strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.
JLS #15.28 lists what is considered as a constant expression. In particular, string literals are constant expressions ("Hel" and "lo") but for a variable to be considered a constant, it needs to be final.
In your case, if you change your code slightly to make str8 and str9 constant, you will get true three times:
final String str8 = "Hel";
final String str9 = "lo";
String hello = "Hello"; // at compile time string is known so in String Constant Pool
String str5 = "Hel" + "lo"; // at compile time string is known so in String Constant Pool same object as in variable hello
String str8 = "Hel"; // at compile time string is known so in String Constant Pool
String str9 = "lo"; // at compile time string is known so in String Constant Pool
String str10 = str8 + str9; // at runtime don't know values of str8 and str9 so in String Constant Pool new object different from variable hello
str10 == hello ? false // as str10 has new object and not the same as in hello
str5 == hello ? true // both were created at compile time so compiler know what's the result in str5 and referenced the same object to str5 as in hello
str10 == str5 ? false // str10 is a different object, hello and str5 are referenced same object as created at compile time.
The code has the following points to consider:
String hello = "Hello";
Here "Hello" is a literal assigned to reference hello thus the literal has its own hashcode
String str5 = "Hel" + "lo";
Here "Hel"+"lo" are 2 literals combined and assigned to reference hello thus the new literal is same as the first one and thus same hashcode
String str8 = "Hel";
String str9 = "lo";
Here str8 + str9 are 2 references which combine and point to a new reference hello thus the new literal has its own hashcode
String str10 = str8 + str9;
System.out.println("str10==hello?" + (str10 == hello));
System.out.println("str5==hello?" + (str5 == hello));
System.out.println("str10==str5?" + (str10 == str5));
when you use == it matches by hash code and value. thus the mismatch.
try to use
string_1.equals(string_2)
instead of
string_1 ==string_2
and you will get value matching only. thus all true
Please refer the below answer also(from What is the difference between == vs equals() in Java?):
The equals() method compares the "value" inside String instances (on the heap) irrespective if the two(2) object references refer to the same String instance or not. If any two(2) object references of type String refer to the same String instance then great! If the two(2) object references refer to two(2) different String instances .. it doesn't make a difference. Its the "value" (that is: the contents of the character array) inside each String instance that is being compared.
On the other hand, the "==" operator compares the value of two object references to see whether they refer to the same String instance. If the value of both object references "refer to" the same String instance then the result of the boolean expression would be "true"..duh. If, on the other hand, the value of both object references "refer to" different String instances (even though both String instances have identical "values", that is, the contents of the character arrays of each String instance are the same) the result of the boolean expression would be "false".
If u compare two strings use string.equals not string1 == string2
try it:
System.out.println("str10==hello?" + (str10.equals(hello));

Unexpected behaviour of String pool

I have the following Test:
public class EqualityTest
{
String one = new String("Hello world");
String two = new String("Hello ") + new String("world");
#Test
public void testStringPool()
{
assertFalse(one == two); // FALSE!!!
assertTrue(one.equals(two));
assertTrue(one.intern().equals(two.intern()));
}
}
I would have expected that due to the String pool nature of Java, the VM would allocated one and two pointing to the same String in the pool. Why is my understanding wrong in this case?
I would have expected that due to the String pool nature of Java, the VM would allocated one and two pointing to the same String in the pool.
Only string constants are interned automatically. So if your code had been:
String one = "Hello world";
String two = "Hello " + "world";
... then one and two would have had the same value. Because you've used new String(...), these expressions aren't constant expressions, and so they aren't interned. (The literals are still interned, of course... but not the strings created from the literals.)
See section 15.28 of the JLS for details on what counts as a constant expression.
Whenever you use new operator, a new object is always created on Heap. So, even if the String "Hello World" is available in the Literal Pool, it will not be referenced. So in fact you are creating 4 objects on Heap: -
Hello World ----> new String("Hello World");
Hello ----> new String("Hello");
World ----> new String("World");
And again, Hello World ----> new String("Hello") + new String("World");
Apart from them, there would be 3 literals created on Literal Pool - "Hello World", "Hello" and "World"
So, 7 objects in total.
On the other hand, had you created your Strings using: -
String str = "Hello World";
String str2 = "Hello " + "World";
Again, here 3 literals would be created on Literal Pool - "Hello World", "Hello " and "World", but the concatenation of the last two literals will reference the first literal.
And hence the two String reference point to same literals, and hence they would be equal now.
Whenever new String ("xyz") is used for creating String object, a new object reference will be created.
For example : when you are dealing with just literals then String pool nature will be used.
String one = "Hello world";
String two = "Hello "+ "world";
System.out.println(one==two);
will print true.

Categories

Resources