When does the "==" operator successfully compare strings? [duplicate] - java

This question already has answers here:
How do I compare strings in Java?
(23 answers)
String literals vs String Object in Java
(6 answers)
Closed 3 years ago.
I understand that the equality operator compares references to the strings. So, it will check to see if the strings refer to the same object and not if they are equal character by character.
As a first step in learning about search algorithms, I set up the following program where I have an array of names and then I check if a certain name appears in the array.
First Approach :
I declare and initialize the array of the names. And I ask the user to input a name to check if it appears in the array.
Here's the code I used -
import java.util.Scanner;
public class Strawman{
public static void main(String[] args){
System.out.println("Enter the name to search for:");
Scanner scanner = new Scanner(System.in);
String key = scanner.nextLine();
String[] names = {"alice", "bob", "carlos", "carol", "craig", "dave", "erin", "eve", "frank", "mallory", "oscar", "peggy", "trent", "walter", "wendy"};
for (int i = 0; i < names.length; i++){
if (key == names[i]) {
System.out.println("Index " + i + " has the name " + key);
}
}
}
}
One of the runs of this program is shown in the following screenshot -
As expected, because I'm using the == operator to compare strings, this fails to find the name "oscar" in the array, even though it appeared in the initial array. This output is as expected based on my understanding of how equality operators compares references of the strings.
But, I don't understand why the program seems to work if instead of asking for user input, I declare the name to search for as a string.
Second Approach:
The name "oscar" to search for has been declared as a string instead of asking for user input -
public class Strawman2{
public static void main(String[] args){
String[] names = {"alice", "bob", "carol", "craig", "carlos", "dave", "eve", "fred", "greg", "gregory", "oscar", "peter"};
String key = "oscar";
for (int i = 0; i < names.length; i++){
if (names[i] == key){
System.out.println("Index " + i + " has name " + key);
}
}
}
}
Now, if I run the program, the name "oscar" is found in the array -
Can someone explain the difference in the two cases?

It's because in the second approach
String key = "oscar";
reuses an instance from the string constant pool populated by
String[] names = {"alice", "bob", "carol", "craig", "carlos", "dave", "eve", "fred", "greg", "gregory", "oscar", "peter"};
Change the way you initiate key variable into:
String key = new String("oscar");
it will behave the same way as first approach as you bypass the String Constant Pool and your key variable will now refer to another object in memory.
For more information about the String Constant Pool:
String Constant Pool

It's because the compiler reuses string instances from string literals that are known at compile time. Hence they pass the object equality check. Reuse is possible because Strings are immutable objects.
Strings that are not know at compile time, and/or explicitly created as new String objects, are not subject to this optimisation and will always result in new objects.

There are only two situations where == is guaranteed to work (as you want) for string testing:
You created a String object explicitly and know sure that you are using the same reference for it in two different places.
You know for sure that both strings you are comparing have been interned. Noting that string literals are always1 interned.
Technically, an interned string is one that is a result of calling String::intern some time during its lifetime. (See JLS 3.10.5 and the javadoc.) Informally an interned string is one that "is in the String pool", though the term "the String pool" is not specified anywhere2.
Anything else and == is liable going to give the wrong answer.
And ... those two cases rarely arise in real-world programs.
1 - Not strictly 100% true: consider literals that are subexpressions in constant expressions. However, that does not affect the behavior of the == operator.
2 - The closest I have found is "a pool of strings, initially empty, is maintained privately by the class String" in the javadocs. But the current javadocs, JLS and JVM spec don't use the phrase "the String pool" or "the String constant pool" or any of the other variations any place that I can find.

Related

Difference between Strings added directly and with references [duplicate]

This question already has answers here:
Java String Instantiation
(3 answers)
Closed 5 years ago.
I came across a question asking the output of the below:
String s1 = "String 1";
String s2 = "String 2";
String s3 = s1 + s2;
String s4 = "String 1" + "String 2";
System.out.println(s3==s4);
Output - false
Now, since the strings are not created using new operator, so the objects are created in the string pool, so as per my understanding s1 + s2 and "String 1" + "String 2" should be equal, and s3==s4 should be true.
But it is not happening in real. Can any one please explain this?
The concatenation is happening at runtime, unless both operands are compile-time constant expressions.
Put a final modifier before s1 and s2, and the result will be true, because the compiler will simply replace
String s3 = s1 + s2;
by
String s3 = "String 1String 2";
If you don't, a new String is created at runtime, by appending both strings to a StringBuilder and getting the result.
Note that, although that is interesting from an intellectual point of view, in practice, you shouldn't care about that performance optimization, and always compare Strings with equals().
This line compare memory addresses of two strings. Because of both are separate objects and output will be false.
s3==s4
You need to compare using equals()
System.out.println(s3.equals(s4));
equals() is compare value of object not address.

How new String() and normal String created ? String class in java (Confusion) [duplicate]

This question already has answers here:
Strange behavior with string interning in Java
(4 answers)
Closed 5 years ago.
I was reading about String in java and was trying to understand it.
At first, it was easy how String s1="11" and String s2=new String ("11") works(created) and I understood intern method also.
But I came across this example (Given by a friend) and made me confused about everything.
I need help to understand this.
String s1 = new String(new String("2")+new String("2"));
s1.intern();
String s2="22";
System.out.print(s1==s2); //=>true as output.
String s3 =new String (new String("2")+new String("2"));
s3.intern();
String s4="22";
System.out.print(s3==s4); //=>false as output.
Answer of this code is true and false.
Part for S1 and s2 was good and was true according to my understanding but the second part I didn't understand.
Hope someone can break the code line by line and help me understand.
s1.intern(); adds s1 to the pool of strings, therefore the string "22" is now in the pool of strings. Therefore when you write s2 = "22" that's the same "22" as s1 and thus s1 == s2.
s3.intern() does NOT add s3 to the pool of strings because the string "22" is already there.
s3.intern() does return that same "22" which is s1 BUT IT IS NOT USED. Therefore s3 is not equal s4.
In java exist the heap and the stack,
Heap is where all Objects are saved
stack is where vars are saved
Now also exist another kind of list for Strings and Integers (numbers)
As you know a String can be created in some ways like
like new String("word") or just = "word" when you use the first way you create a new object (heap) when you use the other you save the word in a stack of words (Java engenniers thought it would be good if you don't create manny objects or words are repeated so they created an special stack for words, same for Integers from 0 to 127) So as I said You have to know that there is an stack and a Heap look at this example
String wordOne ="hola";
String wordTwo = "hola";
String wordTres = "hola";
System.out.println(wordOne == wordTwo);
System.out.println(wordTres == wordTwo);
System.out.println(wordOne == wordTres);
String wordFour = new String("hola");
System.out.println(wordOne == wordFour);
Integer uno = 127;
Integer dos = 127;
System.out.println(uno == uno);
Integer tres = 128;
Integer cuatro = 128;
System.out.println(tres == cuatro);
String x = "word"; is saved in an special Stack
String y = new String("it is not");
But tbh I don't remeber so well the rules for tha stack, but in any case i recomend you to compare all words using wordX.equals(wordY)
An also numbers in objects could be compared using == from 0 to 127 but the same if you use objects use equals, although using numbers there is a better do to do it in spite of use equals, convert one number to a primitive value (the memory will be better)
When you are making string with new keyword,JVM will create a new string object in normal(non pool) heap memory and the literal will be placed in the string constant pool. In your case, The variable s1 will refer to the object in heap(non pool).
String s1 = new String(new String("2")+new String("2"));
But in the next line your are calling intern() method.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
Check Javadocs.
As "22" is not in string pool, a new string literal "22" will be created and a reference of it will be returned. When you are writing:
String s2="22";
it simply refers "22" in string pool. But calling s3.intern() will not create a new string literal as "22" exists in the pool. Check the Javadocs for intern() again. It says if exists in pool, then string from the pool is returned not reference. So, this time s3 references to a different object.
But s4 is referred to same object as s1,s2.
You can print the objects hashcode for checking if the are same or not. Like:
System.out.println(System.identityHashCode(s1));
Notice that the type String is capitalized and is not one of Java's 8 primitive types (int, boolean, double, char, etc.). This indicates that any instance of a String is an object that was built using the 'blueprint' of the class String. Because variables in Java that refer to objects only store the memory address where the actual object is stored, when you compare Strings with == it compares memory location.
String str1 = new String("hello");
String str2 = str1; //sets str1 and str2 pointing to same memory loc
if (str1 == str2){
//do stuff; the code will enter this if-statement in this case
}
The way to compare the values within objects in Java is with equals(), such as:
String str1 = new String("hello");
String str2 = new String("hello"); //str2 not same memory loc as str1
if (str1.equals(str2)){
//do stuff; the code will enter this if-statement in this case
}
This is a common error for beginners, since the primitive types are not objects and you CAN compare two ints for equality like:
int one = 1; //primitive types are NOT objects
int two = 2; //notice when I make an int, I don't have to say "new"
//which means a new **object**
if (int1 == int2) {
//do stuff; in this case the program will not enter this if-statement
}
It seems that you understand everything but the meaning of the very last line. See my comment on the last line.
String s1 = new String(new String("2")+new String("2")); //declare AND initialize s1 as a new String object
s1.intern();
String s2="22"; //declare a new variable s2 and point it to the same object that s1 is pointing to
System.out.print(s1==s2);
String s3 =new String (new String("2")+new String("2"));
s3.intern();
String s4="22";
System.out.print(s3==s4); //check if s3 and s4 are stored in the same memory location = FALSE
In java object1 == object2 means
that do object1 and object2 have the same address in memory?
object1.equals(object2)
means are they equal, for example do they have the same values of all fields?
So, For two Strings S1 and S2,
string1.equals(S2) means, do they have the same characters in the same sequence?
S1 == S1 means are string1 and string2 stored at the same address in memory?

Java checking array contents

The problem I am having is using .contains() for an array. This is my current line of code:
if (strings[i].contains(symbol) = true){
strings[] is an array that stores user inputted data, the error message i get for this is "The left-hand side of an assignment must be a variable". I understand what this means, my question is, can I use one string from the array when using .contains() or am I going about this the wrong way?
Any help will be appreciated. Thanks.
The problem is that
strings[i].contains(symbol) = true
is an assignment because of the =. You probably mean
strings[i].contains(symbol) == true
and, because the left hand side is a boolean,
strings[i].contains(symbol)
is already sufficient.
my question is, can I use one string from the array when using .contains()
If you read the Java API for String (http://docs.oracle.com/javase/7/docs/api/java/lang/String.html), contains() is a method from String class.
By knowing that, you can use .contains() on a single String element / a String variable.
String str = "abc";
str.contains(symbol); //This is ok.
String[] strs = {"abc", "def"};
str[0].contains(symbol); //This is ok too.
Both of the above are allowed, because both are String.
String[] strs = {"abc", "def"};
str.contains(symbol); //This is wrong!
One more thing which you should have noted by now in your codes is:
When comparison, use == and not single =
And when comparing strings or objects, use .equals()
Of course, you can also write it as:
if(strings.[i].contains(symbol)) //if this statement is true
if(!strings.[i].contains(symbol)) //if this statement is not true
instead of
if(strings.[i].contains(symbol) == true) //if this statement is true
if(strings.[i].contains(symbol) == false) //if this statement is not true

Why my below code of String Comparison is not showing what i am expecting in java [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 9 years ago.
if("String ".intern().trim()=="String"){
System.out.println("Equals");
}else{
System.out.println("Unequal");
}
above code is showing Unequal. I don't understand why? Can someone explain it?
I'm not really sure what you're facing, but if you want to compare the addresses of the strings by using the intern() method, this will also work:
if ("String ".trim().intern() == "String") {
System.out.println("Equals");
} else {
System.out.println("Unequal");
}
You only have to swap the trim() and intern() calls.
intern() searches for the string which is already on the heap and addresses this. When you use the == to compare the strings, the addresses will be equal.
For better visualization:
Value Evaluates to Address Explanation
"String" "String" 1 Creates new "String"
"String " "String " 2 Creates new "String "
"String ".trim() "String" 3 trim() creates new "String"
"String ".trim().intern() "String" 1 intern() finds the already created "String"
"String ".intern() "String " 2 intern() finds the already created "String "
"String ".intern().trim() "String" 4 trim() creates new "String"
That's also because trim() is generating a new String().
To try by yourself:
public class StringCompare {
public static void main(String[] args) {
System.out.println(System.identityHashCode("String"));
System.out.println(System.identityHashCode("String "));
System.out.println(System.identityHashCode("String ".trim()));
System.out.println(System.identityHashCode("String ".trim().intern()));
System.out.println(System.identityHashCode("String ".intern()));
System.out.println(System.identityHashCode("String ".intern().trim()));
}
}
Will output something like:
1500673861 // 1
1777631459 // 2
859434349 // 3
1500673861 // 1
1777631459 // 2
538093921 // 4
You need to know that String literals are placed in string pool by default like
String s = "Hello";//this will be in string pool
but Strings created at runtime via new String(...) are not by default placed or retrieved from this pool.
String s2 = new String(s);// or even `new String("Hello")`
System.out.println(s == s2);// returns false, which means s2 holds different String
// object then from String pool
Now substring returns new String(...) objects, so it means they are not from String pool.
trim uses substring method internally which if
range specified to substring will be different than from 0 till length will return new String object with characters from specified range
range is from 0 till strings length (ideally covers entire string) returns same String with return this;
So in your code you are trying to place "String " in string pool (which you don't have to do, because you are using String literal "String " which is already placed in pool), Then you are using trim method on this object so since there is part to trim, it will create and return new String, different from original, and definitely not from String pool so comparing it with "String" literal with == will return false.
"String".intern() creates a different instance of String stored in permgen or metaspace while the original instance is stored on regular heap. Therefore your comparison will always be false.
Always compare strings using equals()method!!!
Try something like this:
if("String ".trim().equals("String")){
System.out.println("Equals");
}else{
System.out.println("Unequal");
}
Useful Link
==, .equals(), compareTo(), and compare()
Comparing Strings and Portions of Strings
When should we use intern method of String on String constants (Example On Stack Overflow)

Behavior of String literals is confusing

The behavior of String literals is very confusing in the code below.
I can understand line 1, line 2, and line 3 are true, but why is line 4 false?
When I print the hashcode of both they are the same.
class Hello
{
public static void main(String[] args)
{
String hello = "Hello", lo = "lo";
System.out.print((Other1.hello == hello) + " "); //line 1
System.out.print((Other1.hello == "Hello") + " "); //line 2
System.out.print((hello == ("Hel"+"lo")) + " "); //line 3
System.out.print((hello == ("Hel"+lo)) + " "); //line 4
System.out.println(hello == ("Hel"+lo).intern()); //line 5
System.out.println(("Hel"+lo).hashCode()); //hashcode is 69609650 (machine depedent)
System.out.println("Hello".hashCode()); //hashcode is same WHY ??.
}
}
class Other1 { static String hello = "Hello"; }
I know that == checks for reference equality and check in the pool for literals. I know equals() is the right way. I want to understand the concept.
I already checked this question, but it doesn't explain clearly.
I would appreciate a complete explanation.
Every compile-time constant expression that is of type String will be put into the String pool.
Essentially that means: if the compiler can (easily) "calculate" the value of the String without running the program, then it will be put into the pool (the rules are slightly more complicated than that and have a few corner cases, see the link above for all the details).
That's true for all the Strings in lines 1-3.
"Hel"+lo is not a compile-time constant expression, because lo is a non-constant variable.
The hash codes are the same, because the hashCode of a String depends only on its content. That's required by the contract of equals() and hashCode().
Strings computed by concatenation at runtime are newly created and therefore distinct
here is a link to read: http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5
String object can be created in the following ways:
String str = new String("abcd"); // Using the new operator
// str is assigned with "abcd" value at compile time.
String str="abcd"; // Using string literal
// str is assigned with "abcd" value at compile time.
String str="ab" + "cd"; // Using string constant expression.
// str is assigned with "abcd" value at compile time.
String str1 = "cd";
String str = "ab"+str1; // Using string expression.
// str is assigned with "abcd" value at run time only.
and Hashcode will be calculated only at runtime based on the contents of the String objects.
It's because the comipler in this instance is not smart enough to work out that it can burn in the same string literal.
Hashcode needs to always return the same value for strings that are equivelent (calling .equals on it returns true) so will return the same result.
Its because following code
("Hel"+lo)) + " "
is translated internally to
new StringBuilder("Helo").append(new String(lo)).append(new String(" ")).toString()
So you can see that entirely a new String instance is created with help of different String instances. That is why you get false as they point to different memory locations in heap
The hashCode doesn't have anything to do with an objects reference (The == check is a reference comparator). Its possible to have 2 objects where the hashCode returns the same value, the equals operator returns true, but == returns false. This is when they are 2 different objects, but with the same value.
I believe the reason line 4 is returning false is that it is a value computed at runtime, and thus is a different string instance, with a different reference.
String literals are saved in a special memory, if they are exactly the same, they are pointed to the same map of memory. If you don't create a literal String, a new object will be created so it won't point to that memory so the reference won't be the same.
The intern() method tells the virtual machine to put it into that shared, string literals map of memory so next time you do that literal, it'll search there and point it.
As you already know ... this is just because of reference ...when string comes from the pool it will have same refrence ...but when u do manuplations a new string with new refrence is generated ...
You can check this link for pooling concept
The difference between line number 3 and 4 are as follows.
•Strings computed by constant expressions are computed at compile time and then treated as if they were literals.
•Strings computed by concatenation at run time are newly created and therefore distinct.
The above reference is taken from java spec. Please let me know if you need more clarification.
http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5
System.identityHashCode() would be returned by the default method hashCode(), this is typically implemented by converting the internal address of the object into an integer.
Finally I know the answer !
Read Java SE 8 specification section 15.21.3 Reference Equality Operators == and != (http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.21.3)
While == may be used to compare references of type String, such an
equality test determines whether or not the two operands refer to the
same String object.
The result is false if the operands are distinct
String objects, even if they contain the same sequence of characters(§3.10.5). The contents of two strings s and t can be tested for
equality by the method invocation s.equals(t).
So the following code :
class Test {
public static void main(String[] args) {
String hello = "Hello";
String lo = "lo";
System.out.println((hello == ("Hel"+lo))); // line 3
}
}
The expression ("Hel"+lo) in line 3, return the new Strings that computed by concatenation at run time.
*Strings computed by concatenation at run time are newly created and therefore distinct.
(http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#d5e1634)
So the result of this code:
class Test {
public static void main(String[] args) {
String hello = "Hello";
String lo = "lo";
System.out.println((hello == ("Hel"+lo))); // line 3
}
}
would result:
false
Because,
the "Hello" object in this expression:
String hello = "Hello";
and ("Hel"+lo) object in this expression:
System.out.print((hello == ("Hel"+lo)) + " ");
is different, although :
*they both contain the same sequence character, which is "Hello".
*they both have the same hashCode.
*hello.equals(("Hel"+lo)) will return true.

Categories

Resources