I've read this answer about how to check if a string is interned in Java, but I don't understand the following results:
String x = args[0]; // args[0] = "abc";
String a = "a";
String y = a + "bc";
System.out.println(y.intern() == y); // true
But if I declare a string literal:
String x = "abc";
String a = "a";
String y = a + "bc";
System.out.println(y.intern() == y); // false
Besides, without any string literal, the args[0] seems to be directly interned:
// String x = "abc";
String y = args[0];
System.out.println(y.intern() == y); // true (???)
// false if the first line is uncommented
Why does y.intern() == y change depending on whether x is a literal or not, even for the example when the command-line argument is used?
I know literal strings are interned at compile time, but I don't get why it affects in the previous examples. I have also read several questions about string interning, like String Pool behavior, Questions about Java's String pool and Java String pool - When does the pool change?. However, none of them gives a possible explanation to this behaviour.
Edit:
I wrongly wrote that in third example the result doesn't change if String x = "abc"; is declared, but it does.
It is because y.intern() gives back y if the string was not interned before. If the string already existed, the call will give back the already existing instance which is most likely different from y.
However, all this is highly implementation dependent so may be different on different versions of the JVM and the compiler.
Implementation details might differ. But this is exactly the behavior I would expect. Your first case means that commandline arguments are not interned by default. Hence y.intern()returns the reference to y after interning it.
The second case is where the VM automatically interns the literal, so that y.intern() returns the reference to x, which is different from y.
And the last case again happens because nothing is interned by default, so the call to intern() returns the reference to y
.
I believe it is legal to intern String more aggressively, but this is the minimal behavior required by the spec as I understand it.
Related
Today while working with String's i have encountered a behavior i don't know before. I'm not able to understand what's happening internally.
public String returnVal(){
return "5";
}
String s1 = "abcd5";
String s2 = "abcd"+"5";
String s3 = "abcd5";
String s4 = "abcd"+returnVal();
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
System.out.println(s3 == s4);
System.out.println(s3.equals(s4));
My expectation is printing "true" from all s.o.p's but s3 == s4 is false, why?
My expectation is printing "true" from all s.o.p's but s3 == s4 is false, why?
The compiler can do constant expression inlining. This means that
String s1 = "abcd5";
String s2 = "abcd"+"5";
final String five = "5"; // final reference
String sa = "abcd" + five;
are all the same (except five) and the compiler can simplify all these expressions to "abcd5"
However, if the compiler cannot optimise the expression, the operation is performed at runtime and a new String is created. This new String is not a constant which is places in the String literal pool (as it is not a literal in byte code)
String s4 = "abcd" + returnVal(); // not inlined by the compiler.
String f5 = "5"; // not a final reference.
String sb = "abcd" + f5; // evaluated at runtime
These create new strings every time they are run (as well as new StringBuilder and char[]s)
You have stumbled across the intricacies of how the Java compiler optimizes String.
Suppose I have this program:
String a = "abc";
String b = "abc";
Here the compiler can initialize a and b to the same String instance. This entails that a == b and a.equals(b).
Here we also get the same behaviour:
String a = "abc";
String b = "ab" + "c";
This is because "ab" + "c" can be evaluated at compile-time to just "abc", which in turn can share an instance with a.
This technique is not possible with expressions that call functions:
String a = "abc";
String b = "ab" + functionThatReturnsC();
This is because functionThatReturnsC could have side-effects which cannot be resolved at compile-time.
Your case of returnVal is interesting. Since it is constant, it could be inlined, in which case the compile-time instance sharing could be applied. It seems the compiler implementer decided not to support this.
This issue exposes a weakness of Java. Since we cannot override =, programmers cannot implement custom value-types. Therefore, you should always use equals or Objects.equals to ensure consistent behaviour.
Note that these optimizations may differ between compilers.
This question already has answers here:
String Constant Pool
(5 answers)
Closed 7 years ago.
In this example I thought that the result was true. I thought that the variable stored in the string pool.
The answer was: returns false because the two String objects are not the same in memory. One comes directly from the string pool and the other comes from building using String operations.
String a = "";
a += 2;
a += 'c';
a += false;
if ( a == "2cfalse") System.out.println("==");
I do not understand where the variable a was stored
Okay, so two responses to this. First, the ethically correct one, do never test strings with ==, always use .equals() or .equalsIgnoreCase().
Secondly, it's true that indeed, "a" == "a" because the strings are stored in, as you call it, the same pool. The problem here is that you append to it. Appending to a string causes it to become a different string, which is not stored in the string pool. The string pool is only generated on-compile, and as the second string is calculated on runtime, it won't match the one generated on-compile.
Imagine a string-pool to work like this:
a = "test";
b = "te";
c = "st";
d = "test";
The compiler translates this into
sp1 = "test";
sp2 = "te";
sp3 = "st";
a = sp1;
b = sp2;
c = sp3;
d = sp1;
Now == will check if two variables refer to the same sp. If you run b + c java will not go back and check if any of the sp's is the same as that. It only does that on compile.
This question already has answers here:
Comparing strings with == which are declared final in Java
(6 answers)
What is the difference between these two ways of initializing a String?
(3 answers)
Closed 7 years ago.
I am unable to recognize the difference in the following declarations of Strings in Java.
Suppose I am having two string
String str1="one";
String str2="two";
What is the difference between
String str3=new String(str1+str2);
and
String str3=str1+str2;
In both the above declarations, the content of str3 will be onetwo.
Suppose I create a new string
String str4="onetwo";
Then in none of the above declarations,
if(str4==str3) {
System.out.println("This is not executed");
}
Why are str3 and str4 not referring to the same object?
str1 + str2 for non-compilation-constant strings will be compiled into
new StringBuilder(str1).append(str2).toString(). This result will not be put, or taken from string pool (where interned strings go).
It is different story in case of "foo"+"bar" where compiler knows which values he works with, so he can concatenate this string once to avoid it at runtime. Such string literal will also be interned.
So String str3 = str1+str2; is same as
String str3 = new StringBuilder(str1).append(str2).toString();
and String str3 = new String(str1+str2); is same as
String str3 = new String(new StringBuilder(str1).append(str2).toString());
Again, strings produced as result of method (like substring, replace, toString) are not interned.
This means you are comparing two different instances (which store same characters) and that is why == returns false.
Java does not have memory of "how this variable got the value", therefore it really does not matter which method you use, if the result is same.
About comparing, if you compare strings with ==, you are comparing address of objects in memory, because String is not primitive data type, not values. You have to use if(str4.equals(str3))
Because Strings in Java are immutable the compiler will optimize and reuse String literals. Thus
String s1 = "one";
String s2 = "one";
s1 == s2; //true because the compiler will reuse the same String (with the same memory address) for the same string literal
s1 == "o" + "ne"; //true because "Strings computed by constant expressions are computed at compile time and then treated as if they were literals"
s3 = "o";
s1 == s3 + "ne"; //false because the second string is created a run time and is therefore newly created
for a reference see http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5
Strings are kind of tricky, because there is some effort to share their representation. Plus they're immutable.
The short answer is: unless you're really working in a low level, you should never compare strings using "==".
Even if it works for you, it will be a nightmare for your teammates to maintain.
For a longer answer and a bit of amusement, try the following:
String s1= "a" + "b";
String s2= "a" + "b";
String s3=new String("a"+"b");
System.out.println(s1==s2);
System.out.println(s3==s2);
You'll notice that s1==s2 due to the compiler's effort to share.
However s2 != s3 because you've explicitly asked for a new string.
You're not likely to do anything very smart with it, because it's immutable.
This question already has answers here:
String comparison and String interning in Java
(3 answers)
How do I compare strings in Java?
(23 answers)
String.equals versus == [duplicate]
(20 answers)
Closed 10 years ago.
I'm new to this site and didn't realize there were other questions that answered what I'm asking. I've figured it out and I will delete this post as soon as it lets me. Thank you.
I just started learning java again and I have a quick question.
Usually, using == to compare strings would not work and you would have to use .equals instead.
But now while coding, I found they are doing the same thing when they are not supposed too and I'm trying to figure out why.
Here's the code.
String s = "Hello";
String x = "Hello";
if (x == s){
System.out.println("It is working!");
}//end if
else {
System.out.println("It is not working");
}//end else
if (x.equals(s)){
System.out.println("Match");
}//end if
else {
System.out.println("No match");
}//end else
Basically you're seeing the result of string interning. From section 15.28 of the JLS:
Compile-time constant expressions of type String are always "interned" so as to share unique instances, using the method String.intern.
So your two variable values actually refer to the same strings. If you use:
String s = new String("Hello");
String x = new String("Hello");
... then you'll see the behaviour you expect.
Reason is that your vars x and s have the same reference hence == behaves same as .equals.
When Java compiler optimizes your string values (literals), it determines that both x and s have same value and hence you need only one String object. As result, both x and s point to the same String object and some little memory is saved. It's safe operation since String is an immutable object in Java.
well, In this particular case there is only one string object created and cached in the string constant pool and both of the reference variables refer to the same string object which is stored in the string constant pool . thus you == test passes
String s = "Hello"; Line 1
String x = "Hello"; Line 2
When line 1 is executed one string object (Hello) is created and cached in String Constant pool .
**String Constant Pool:** {s--->"Hello"}
when line 2 is executed, it first checks if there is any object with the same value in string constant pool, if there exists one. it simply points the reference to the already created object in the pool.
**String Constant Pool:** {s,x--->"Hello"}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
difference between string object and string literal
When initializing a String object there are at least two ways, like such:
String s = "some string";
String s = new String("some string");
What's the difference?
The Java language has special handling for strings; a string literal automatically becomes a String object.
So in the first case, you're initializing the reference s to that String object.
In the second case, you're creating a new String object, passing in a reference to the original String object as a constructor parameter. In other words, you're creating a copy. The reference s is then initialized to refer to that copy.
In first case you can take this string from pool if it exist there.
In second case you explicitly create new string object.
You can check this by these lines:
String s1 = "blahblah";
String s2 = "blahblah";
String s3 = new String("blahblah");
String s4 = s3.intern();
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
System.out.println(s1 == s4);
Output:
true
false
false
true
String s = "some string"; assigns that value to s from string pool (perm.gen.space) (creates one if it does not exist)
String s = new String("some string"); creates a new string with value given in constructor, memory allocated in heap
The first method is recommended as it will help to reuse the literal from string pool
Semantically, the first one assigns "some string" to s, while the second one assigns a copy of "some string" to s (since "some string" is already a String). I see no practical reasons to do this in 99.9% of cases, thus I would say that in most contexts, the only difference between the two lines is that:
The second line is longer.
The second line might consume more memory than the first one.
As #Anish-Dasappan mentions, the second one will have it's value in heap, whereas the first one will be in the string pool - I'm not sure this has any interest for the programmer, but I might be missing a clever trick there.