Java HashMap key comparison against string fails - java

I have the following situation. I have a HashMap in Java with keys as strings.
Then in some stage , in runtime I create strings equal to those keys in order to retrieve the data from that map.The strings are created as follows within "for" loop:
String keyToRetrive = "lights[" + Integer.toString(i) + "]" + ".Intensity";
The strange thing about it that when I iterate through the map to find the key that equals that string ,even when the match is found the search steps over.So in this search loop :
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
if (name == entry.getKey()) { ///name- "lights[0].Intesity"
uniformOut = (ICleanable) entry.getValue();
break;
}
}
The key with the name "lights[0].Intesity" never returns true even that the map contains one.How I solved it .I used hashCode() for both compared string values.So this version does work:
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
if (name.hashCode() == entry.getKey().hashCode()) {
uniformOut = (ICleanable) entry.getValue();
break;
}
}
UPDATE: After being pointed to the fact that "==" doesn't work good and "equals()" should be used I would like to narrow the question:Why "==" does work for strings which were not created from several concatenated blocks? I mean, if I defines key string to compare agains as a simple single string:
String foo="foo";
Such a string is compared using "==" against HashMap key all right.
I am not an expert Java programmer so can anybody explain why it works this way?

You are comparing Strings using == operator. Use equals() instead:
name.equals(entry.getKey())
This is a common pitfall in Java, see How do I compare strings in Java? and Difference between Equals/equals and == operator?.
BTW (unrelated to your problem) when concatenating strings you don't need to call toString() explicitly so this:
"lights[" + Integer.toString(i) + "]" + ".Intensity"
can be replaced with:
"lights[" + i + "]" + ".Intensity"
It'll work for i of any type, not only int.

When you compare objects using ==, you're performing a "referential equality" comparison, meaning that you're checking whether the two references point at the same String object in memory. If you're familiar with C, it would be like:
char* a = some_string();
char* b = some_other_string();
if (a == b) { ... }
On the other hand, when you compare objects using .equals(), you're performing a "structural equality" comparison, meaning that you're checking whether the two objects contain equivalent data. Again, the C analog of this would be:
char* a = some_string();
char* b = some_other_string();
if (strcmp(a, b) == 0) { ... }
Now, the thing you really, really don't want to do is to compare the two objects' hash codes. Why not? Because two objects with the same hash code are not necessarily equal! They might be, but you can't correctly rely on it.
Update: You also asked about why == works for string literals. The answer is because the Java compiler doesn't allocate constant strings on the heap; instead it stores them in the constant pool of the class in which they're used. So, if you write:
String foo1 = "foo";
String foo2 = "foo";
Then the compiler will have both references point at the same location in the class's constant pool. If, however, you write:
String foobar1 = "foobar";
String foobar2 = "foo" + bar();
String bar() { return "bar"; }
The compiler isn't quite smart enough to figure out that foobar2 is logically equivalent to foobar1. However, even if you know that the two variables are compile-time constants, you still should keep it simple and use .equals().

The others have stated why your code won't work, however:
1) If you're using a HashMap, you should use map.get(key) to retreive the value, not an interator of entries; that's the whole point of the Hash Map.
2) Use generics, avoid explicitly casting as much as you can!

Related

Garbage Collection for String Class in Java

In this code I have declared a Initialized a String variable and then printed its hashcode, then reinitialized it to another value and then invoked the Garbage Collector to clear the dereferenced objects.
But when I reinitialize the String variable to its original value and print the hashcode, the same hashcode is getting printed. How?
public class TestGarbage1 {
public static void main(String args[]) {
String m = "JAVA";
System.out.println(m.hashCode());
m = "java";
System.gc();
System.out.println(m.hashCode());
m = "JAVA";
System.out.println(m.hashCode());
}
}
Hash code relates to object equality, not identity.
a.equals(b) implies a.hashCode() == b.hashCode()
(Provided the two methods have been implemented consistently)
Even if a gc were actually taking place here (and you weren't simply referencing strings in the constant pool), you wouldn't expect two string instances with the same sequence of chars not to be equal - hence, their hash codes will also be the same.
String a = new String("whatever");
String b = new String(a);
System.out.println(a == b); // false, they are not the same instance
System.out.println(a.equals(b)); // true, they represent the same string
System.out.println(a.hashCode() == b.hashCode()); // true, they represent the same string
I think you are misunderstanding something about how hashcodes work. Without going in to too much detail, in Java, hashcodes are used for many things. One example is used to find an item in a Hash datastructure like HashMap or HashSet.
A hash of the same value should always return the same hash. In this case, a hash of "JAVA" should never change because then it will break the agreement set forth in Java.
I think it's too complicated to go about how hashcodes for String are calculated. You can read more about it here. I can give you an example though.
Let's say you have a class Fruit and it has fields like shape, color and weight.
You must implement equals AND hashcode for this class. It is very important to do both because otherwise you are breaking the way Hashmap work. Let's say you make this for your hashCode() method.
#Override
public int hashCode() {
int hash = 1;
hash = hash * 17 + this.color;
hash = hash * 31 + this.shape.hashCode();
hash = hash * 31 + this.weight;
return hash;
}
This will generate the same hash value EVERY TIME for the two Fruit instances that are equal. That is exactly what you would want.
Really quick, how would this be actually used in a HashMap? Let's say you want to see if you have foo = new Fruit(); HashMap first calculates foo.hashCode(). It checks to see if there is anything in the bucket for that hashCode. If there is then it will use the equals() method until it returns true. It must do this because there might be hashcode collisions. And that's why it is important why equals and hashCode should be implemented together.

how to check reference equality in an object which implements content equality?

...in other words:
let's suppose I have 2 Strings declared as so:
String one = new String("yay!");
String two = new String("yay!");
these two Strings are two different objects, but if I run
if(one.equals(two))
System.out.println("equals() returns true.");
I get "equals() returns true".
This is because the String class overrides the equals() method to implement a content level equality.
However, I need to access a reference level equality (like the one implemented in Object) to distinguish the object one form the object two.
How can I do that?
I tried this:
one.getClass().getSuperclass().equals();
to try to invoke the Object equals() method of the String one but it didn't work.
Any advice?
If you want to check reference just perform:
one == two
But be careful with strings. There is a thing called String constant pool so they may refer to the same object.
String in java uses a String Literal Pool, this means is: "When you try construct a string, first String class search in Literal Pool for traditional same string ,if exist return it, and if don't exist create it", so you can't check by equals method compare refernce of String instance, you have to use == operator as following:
String one = new String("yay!");
String two = new String("yay!");
if(one.equals(two))
System.out.println("equals() returns true.");
if(one == two)
System.out.println(" == operator returns true.");
result is :
equals() returns true.
see following link for more information:
http://blog.enrii.com/2006/03/15/java-string-equality-common-mistake/
Java String.equals versus ==
Use simple == comparison. However to avoid String interning you have to create your Strings using char arrays such as: String me = new String(new char[] { 'm', 'e' }); instead of using String literals "me" such as String me = new String("me");.
if (one == two)
System.out.println("one and two are the same object");
The only this you need is "==" equality operator.

How to compare Integer correctly in Java [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Integer wrapper class and == operator - where is behavior specified?
I known Java integer use cache in -127~128.
If
Integer i = 1;
Integer j = 1;
Integer m = 128;
Integer n = 128;
i == j // true
m == n // false
But I met a strange phenomenon.First,look at following snippet.
List<CustomerNotice> customerNotice = findByExample(example); // use Hibernate findByExample method
for(CustomerNotice n : customerNotice){
if(n.getConfirmStatus() == NoticeConfirmStatus.UNCONFIRMED.getValue()){
// do sth
}
}
public enum NoticeConfirmStatus{
UNCONFIRMED(1), //
CONFIRMED(2), //
FAILED_TO_CONFIRM(3); //
private final Integer value;
private NoticeConfirmStatus(Integer value) {
this.value = value;
}
public Integer getValue() {
return this.value;
}
}
public class CustomerNotice {
#Column(name = "CONFIRM_STATUS")
private Integer confirmStatus;
public Integer getConfirmStatus() {
return this.confirmStatus;
}
public void setConfirmStatus(Integer confirmStatus) {
this.confirmStatus = confirmStatus;
}
}
Although the if expression is not recommended, I think it will be return true,because n.getConfirmStatus()==1, but the result is false.I'm very confusing.
In addition, theList<CustomerNotice> customerNotice acquired by Hibernate findByExample method. Is there some Autoboxing or new operation when retrieve the resultset?
Thank you.
SHORT: (answers question)
If you want to compare Integers as the objects, you should use .equals:
i.equals(j);
m.equals(n);
With this, they should both return true. But if you really want to use ==, you need to get the primitive int value:
i.intValue() == j.intValue();
m.intValue() == j.intValue();
LONG: (explains answer)
The basis of this is that Objects are always stored separately in memory (except for some special cases like m=n), and to be compared properly, they need to be broken down into primitive types that can be compared successfully using ==.
Every Object has a .equals() method, which is inherited from Object as its superclass. However, it must be overridden to do a proper comparison. Integer overrides this method to compare to Integer objects successfully, while using == checks to see if both objects point to the same space in memory, and because two instances of an Object cannot point to the same space in memory, this will always return false.
However, as your code points out, there are some special cases that work, like these:
Your code uses a Integer i = 1, which is considered a "standard instance" and is able to be compared using ==.
If you set one Object equal to another using =, Java tells both objects to point to the same location in memory, which means that == will return true.
There are many others, but those are the two that come to mind and seem relevant.
You'll drive yourself crazy and waste a lot of time trying to figure out specific cases where this works or does not work. It depends on the implementation of code which isn't always visible to you.
The bottom line: never, ever, use == to compare Integer instances, period. As you have seen, it works sometimes, under some circumstances, and fails miserably the rest of the time. If you have a method that returns an Integer, then assign the value to an int, and then you can use == to compare that int to another int.

Comparing two identical strings with == returns false [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 9 years ago.
I am making an archive for my family. There are no syntax errors, however whenever I type in "Maaz", it evaluates realName == "Maaz" to false and goes to the else statement.
import java.util.Scanner;
public class MainFamily {
public static void main (String [] args) {
System.out.println("Enter you're name here");
Scanner name = new Scanner(System.in);//Scanner variable = name
String realName;
realName = name.nextLine();//String variable = user input
System.out.println("Name: "+ realName);
if (realName == "Maaz") {
System.out.println("Name: Maaz");
} else {
System.out.println("This person is not in the database");
}
}
}
TL;DR
You wrote (this doesn't work):
realName == "Maaz"
You meant this:
realname.equals("Maaz")
or this:
realname.equalsIgnoreCase("Maaz")
Explanation
In Java (and many other Object-Oriented programming languages), an object is not the same as a data-type. Data-types are recognized by the runtime as a data-type.
Examples of data-types include: int, float, short.
There are no methods or properties associated with a data-type. For example, this would throw an error, because data-types aren't objects:
int x = 5;
int y = 5;
if (x.equals(y)) {
System.out.println("Equal");
}
A reference is basically a chunk of memory that explicitly tells the runtime environment what that data-block is. The runtime doesn't know how to interpret this; it assumes that the programmer does.
For example, if we used Integer instead of int in the previous example, this would work:
Integer x = new Integer(5);
Integer y = new Integer(5);
if (x.equals(y)) {
System.out.println("Equal");
}
Whereas this would not give the expected result (the if condition would evaluate to false):
Integer x = new Integer(5);
Integer y = new Integer(5);
if (x == y) {
System.out.println("Equal");
}
This is because the two Integer objects have the same value, but they are not the same object. The double equals basically checks to see if the two Objects are the same reference (which has its uses).
In your code, you are comparing an Object with a String literal (also an object), which is not the same as comparing the values of both.
Let's look at another example:
String s = "Some string";
if (s == "Some string") {
System.out.println("Equal");
}
In this instance, the if block will probably evaluate to true. Why is this?
The compiler is optimized to use as little extra memory as is reasonable, although what that means depends on the implementation (and possibly runtime environment).
The String literal, "Some string", in the first line will probably be recognized as equivalent to the String literal in the second line, and will use the same place in memory for each. In simple terms, it will create a String object and plug it into both instances of "Some string". This cannot be relied upon, so using String.equals is always a better method of checking equivalence if you're only concerned with the values.
do this instead
if (realName.equals("Maaz"))
equals() should be used on all non-primitive objects, such as String in this case
'==' should only be used when doing primitive comparisons, such as int and long
use
if(realName.equals("Maaz"))
use == with primitive data type like int boolean .... etc
but if you want to compare object in java you should use the equals method
You have to compare objects with realName.equals ("Maaze"), not with ==.
It is best practice to compare Strings using str.equals(str2) and not str == str2. As you observed, the second form doesn't work a lot of the time. By contrast, the first form always works.
The only cases where the == approach will always work are when the strings are being compared are:
string literals or references to string literals, or
strings that have been "interned" by application-level code calling str = str.intern();.
(And no, strings are not interned by default.)
Since it is generally tricky to write programs that guarantee these preconditions for all strings, it is best practice to use equals unless there is a performance-related imperative to intern your strings and use ==.
Before that you decide that interning is a good idea, you need to compare the benefits of interning with the costs. Those costs include the cost of looking up the string in the string pool's hash table and the space and GC overheads of maintaining the string pool. These are non-trivial compared with the typical costs of just using a regular string and comparing using equals.
You can also use
realname.equalsIgnoreCase("Maaz")
This way you can accept Maaz, maaz, maaZ, mAaZ, etc.
== tests shallow equality. It checks if two objects reference the same location in memory.
Intriguing. Although, as others have stated, the correct way is to use the .equals(...) method, I always thought strings were pooled (irrespective of their creation). It seems this is only true of string literals.
final String str1 = new String("Maaz");
final String str2 = new String("Maaz");
System.out.println(str1 == str2); // Prints false
final String str3 = "Laaz";
final String str4 = "Laaz";
System.out.println(str3 == str4); // Prints true
Since you are working on strings, you should use equals to equalsIngnorecase method of String class. "==" will only compare if the both objects points to same memory location, in your case, both object are different and will not be equal as they dont point to same location. On the other hand, equals method of String class perform a comparison on the basis of the value which objects contains. Hence, if you will use equals method, your if condition will be satisfied.
== compares object references or primitive types (int, char, float ...)
equals(), you can override this method to compare how both objects are equal.
for String class, its method equal() will compare the content inside if they are the same or not.
If your examples, both strings do not have the same object references, so they return false, == are not comparing the characters on both Strings.
It seems nobody yet pointed out that the best practice for comparing an object with a constant in Java is calling the equals method of the constant, not the variable object:
if ("Maaz".equals (realName)) {}
This way you don't need to additionally check if the variable realName is null.
if(realName.compareTo("Maaz") == 0) {
// I dont think theres a better way do to do this.
}

Creating Strings from Bytes/Ints in Java

I'm wondering why the following code doesn't work:
String test = new String(new byte[] {92, 92, 92, 92, 92});
System.out.println(test);
String compare = "\\\\\\\\\\";
System.out.println(compare);
if (test == compare) {
System.out.println("Yes!");
}
The output is:
\\\\\
\\\\\
Where is a data type conversion happening that I'm not understanding?
Edit: /fail :(
Strings are compared with .equals(), not with ==
The reason is that with references (as string variables are), == just checks equality in memory location, not in content.
The literal \\\ existed in one place in memory. the other one is created somewhere else where you build the string. They're not in the same location, so they don't return true when you do ==
You should do
if(test.equals(compare))
Strings in Java are reference types, and == checks whether they are the same string, rather than equal strings. Confusing I know. Long story short you need to do this:
if( test.equals( compare ) ) {
For more you can see here: http://leepoint.net/notes-java/data/strings/12stringcomparison.html
You are testing to see if those are the same object, not if they are equal strings.
However the following test will be true:
test.intern() == compare.intern()
You are using identity comparison, rather than string comparison.
Try test.equals(compare). Then try test.intern() == compare. Both should work. The intern method is the only reliable way to perform object identity comparisons on String objects.

Categories