I'm trying to get a value with the key. I'm using get() method. My key is an object composed with int and String. So I make and object
HashMap<Keys,String> test = readFile(fileName2);
Keys myKey = new Keys(2,"SM");
test.get(myKey);
And I received null. When I look at debbuging mode or when I print keySet I received something like this
[Keys#d9c6e2, Keys#10285d8, Keys#c3dd7e]
although my key should be
[1,"GM", 2,"SM", 3"PM"]
why the key look like this Keys#d9c6e2 instead of 2,"SM"? and how to get the value with the key 2,"SM"?
I override toString methid in Keys . It looks better but still i have null value and im sure there is some value.
Keys myKey = new Keys(2,"GL-G--T");
System.out.println(myKey.toString());
System.out.println(test.get(myKey.toString()));
Set keyset = test.keySet();
System.out.println(keyset);
2,GL-G--T
null
[3,PNSN--G, 2,GL-G--T, 1,SM]
You need to override toString method on your Keys object. Otherwise you will get the default toString provided by java.lang.Object.
You could implement the toString method to look something like this:
public class Keys {
private final Integer i;
private final String s;
public Keys(Integer i, String s) {
this.i = i;
this.s = s;
}
#Override
public String toString() {
return i + "," + s;
}
}
if you want the quotes to be displayed then you'd need to provide those:
return i + ",\"" + s + "\"";
You'll also need to override the equals and hashCode for this object to be used as a key in a map:
#Override
public boolean equals(Object o) {
if (!(o instanceof Keys)) {
return false;
}
Keys other = (Keys)o;
return other.s.equals(s) && other.i.equals(i);
}
#Override
public int hashCode() {
return toString().hashCode();
}
If you don't override equals and hashcode, then the map uses the default implementations, which results in two Keys objects with the same values being unequal.
You could as you are doing use a special Keys object as the key to your hash map-- you then just need to correctly implement hashCode and equals on that Keys class as others have explained.
Unless you have a specific reason not to, though, you really could just use a String as the key to the hash map. Create some method such as the following:
private static String getHashMapKeyFor(int intKey, String stringKey) {
return stringKey + "|" + intKey;
}
and declare your hash map as taking a String as the key type. Then, whenever you want to put/find a value in the hash map, call the above method first to get the actual key to use to the hash map.
Using the custom object class may have a superficial air of "correctness" or "engineeredness" to it, but in reality, just using a String will generally perform equally well and if anything may even save slightly on memory.
In your Keys.java object override the toString method. Currently it's using the method defined in java.lang.Object#toString
Related
I have a method that is expected to save an object in a hashmap (used as a cache) that has as a key a String.
The objects that are passed in the method have either fields that are “volatile” i.e. they can change on a next refresh from the data store or are the same across all objects except for 3 fields.
Those fields are 2 of type double and 1 field of type String.
What I am doing now is I use:
Objects.hash(doubleField1, doubleField2, theString)
and convert the result to String.
Basically I am generating a key for that object based on the immutable state.
This seems to work but I have the following question:
If we exclude the case that we have 2 objects with the exact same fields (which is impossible) how likely is that I could end up with a collision that won’t be able to be verified properly?
I mean that if I have a hashmap with keys e.g. strings etc if there is a collision on the hashCode the actual value of the key is compared to verify if it is the same object and not a collision.
Would using keys the way I have described create problems in such verification?
Update:
If I have e.g. a hashmap with key a Person and the hashCode is generated using fullName and ssn or dateOfBirth if there is a collision then the hashmap implementation uses equals to verify if it is the actual object being searched for.
I was wondering if the approach I describe could have some issue in that part because I generate the actual key directly
Here is a simple demo for a hashMap key implementation. When retrieving the object I construct the fields piecemeal to avoid any possibility of using cached Strings or Integers. It makes a more convincing demo.
Map<MyKey, Long> map = new HashMap<>();
map.put(new MyKey(10,"abc"), 1234556L);
map.put(new MyKey(400,"aefbc"), 548282L);
int n = 380;
long v = map.get(new MyKey(n + 20, "ae" + "fbc")); // Should get 548282
System.out.println(v);
prints
548282
The key class
class MyKey {
privat eint v;
private String s;
private int hashcode;
public MyKey(int v, String s) {
Objects.requireNonNull(s, "String must be provided");
this.v = v;
this.s = s;
// this class is immutable so no need to keep
// computing hashCode
hashcode = Objects.hash(s,v);
}
#Override
public int hashCode() {
return hashcode;
}
#Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null) {
return false;
}
if (o instanceof MyKey) {
MyKey mk = (MyKey)o;
return v == mk.v && s.equals(mk.s);
}
return false;
}
}
I am new to Java (very new).
I am trying to understand HashMap and the equals method of a class and how it overrides the duplicates.
Please see following code:
public class Student {
Integer StudentId;
String Name;
String City;
public Student(Integer studentId, String name, String city) {
super();
StudentId = studentId;
Name = name;
City = city;
}
public Integer getStudentId() {
return StudentId;
}
public String getName() {
return Name;
}
public String getCity() {
return City;
}
#Override
public int hashCode() {
System.out.println("haschode is called for " + this);
final int prime = 31;
int result = 1;
result = prime * result + ((StudentId == null) ? 0 : StudentId.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
System.out.println("equals is called for " + this);
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (StudentId == null) {
if (other.StudentId != null)
return false;
} else if (!StudentId.equals(other.StudentId))
return false;
return true;
}
#Override
public String toString() {
return "\n Student [StudentId=" + StudentId + ", Name=" + Name + ", City=" + City + "] \n";
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<Student, String> myMap = new HashMap<Student, String>();
myMap.put(new Student(1, "andy", "p"), "Great"); //Line 1
myMap.put(new Student(2, "sachin", "m"), "Better");
myMap.put(new Student(3, "dev", "s"), "Good");
myMap.put(new Student(1, "andy", "p"), "Excellent"); // Line 4
System.out.println(myMap);
}
}
Now, the code written in main() calls the equals method only when I write the code to put the same key again i.e. "Line 4" (see my code indentation).
Why is the equals method not called for "Line 2" and "Line 3"??
It should call equals for every put line .... correct?
I am missing some understanding here and am left with questions:
(1) Why is every put not calling the equals method to check the equality of class members?
(2) Who triggers the call of the Student class equals method?
It should call equals for every put line .... correct ?
No. A HashMap will call equals only after it encounters a hash collision between an existing key and the one given in put.
Rephrased, it calls hashCode first to determine which "hash bucket" to put the key into, and if there are already keys inside the target bucket, it then uses equals to compare the keys in the bucket for equality.
Since the value of Student.hashCode() is based on ID alone, during insertion, the map only needs to call equals when it encounters a Student key with the same ID as what is being inserted. If no existing keys have the same hashCode as the one being inserted, there is no need to call equals.
This makes HashMap very efficient during insertion. This is also why there is a contract between hashCode and equals: If two objects are equal as defined by equals, they must also have the same hashCode (but not necessarily vice-versa).
equals() is not called if the hashCode() result is different. It's only the same for Line 1 and Line 4 (same student Id of 1), so equals() is called for that.
Note that hashCode() may be the same for two objects that aren't equals(), but two equals() objects must never have a different hashCode():
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
So the initially different hash code is enough to not call equals() afterwards.
The whole purpose of a hash-based map is to operate on hash values (for efficiency that is).
The Map first and foremost cares about different hash values. Thus, as long as any "incoming" key has an (so far) unknown hash, equality doesn't matter.
Only when you run into a conflicting hash, then it matters whether that incoming key is actually a different key, or the same key. In the first case, you add a new key/value pair to the map, in the second case, you update an already stored key with a potential new value!
Therefore calling equals() only happens for situations where the Map implementation has to decide whether two keys that have the same hash are equal, or not.
If hash code differs, then there is no case for calling equals. Look at the code for HashMap(). If the hash is the same, then equals is called.
As you can see while running your code, hashcode() is called for every .put() call.
Basicly hashcode is called for every put() operation, if it is unique then a new element can be placed in the map - as one of the conditions for the hashcode() says, that different hashcodes always represent different objects. However, different object don't always have different hashcodes. Because of that, if the hashcodes are the same for two objects, hashmap have to check object equality with equals().
If you want to watch how the hashCode and equals methods work for selected values,
create the following map:
Map<Key, Value> map = new HashMap<>();
Then create instances of the following classes and use them to populate the Map. I recommend using a String as the object of both classes since I used its equals method in both.
Notice that you supply the hashCode to be returned. This allows it to be the same or different so you can see how the map behaves in different situations.
class Value {
private Object obj;
private int hashCode;
public Value(Object obj, int hashCode) {
this.obj = obj;
this.hashCode = hashCode;
}
public int hashCode() {
System.out.println("Value: hashCode is called - " + hashCode);
return hashCode;
}
public boolean equals(Object o) {
System.out.println("Value: equals is called - " + obj);
return obj.equals(o);
}
public String toString() {
return "Value: obj = " + obj + ", hashCode = " + hashCode;
}
}
class Key {
private Object obj;
private int hashCode;
public Key(Object obj, int hashCode) {
this.obj = obj;
this.hashCode = hashCode;
}
public int hashCode() {
System.out.println("Key: hashCode is called - " + hashCode);
return hashCode;
}
public boolean equals(Object o) {
System.out.println("Key: equals is called - " + obj);
return obj.equals(o);
}
public String toString() {
return "Key: obj = " + obj + ", hashCode = " + hashCode;
}
}
You can read the source code of HashMap.java in JDK 1.7.
Than you will understand the questions you asked.
Other answers are more helpful after you have reading the source code of HashMap.
I have a program to search the key to print the values from a hashmap. But my inputs to the Key and Values are objects that are user defined.Now when I'm equating input key with key1 why are the hashcodes of the Objects key and key1 in the program appearing different, although the return type is same,ie. NameInit, where the hashcodes of the String str="abc" and abc are returned equal? How to check the equality of key and key1 in the program? I tried Objects.equals(key,key1) after type-casting to Object class, but still did not work.I have seen questions of similar kind like in [this question][1] that discusses about the hashcode equality, but then again how to do the equality of these objects as in my example. Kindly help.
NameInit Class
public class NameInit {
String name;
public NameInit(String name)
{
this.name = name;
}
#Override
public String toString(){
return name;
}
}
PlaceAndAddInit
public class PlaceAndAddInit {
String place;
int value;
public PlaceAndAddInit(String place,int val) {
this.place = place;
this.value= val;
}
#Override
public String toString(){
return place+" "+value;
}
}
Main Class
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
HashMap h = new HashMap();
System.out.println("Limit: ");
int limit = scan.nextInt();
for(int i=0;i<limit;i++)
{
h.put(new NameInit(scan.next()), new PlaceAndAddInit(scan.next(),
scan.nextInt()));
}
System.out.println("Enter a key to search the value: ");//
NameInit key= new NameInit(scan.next());//asks for a input from the user to fetch the values
Set s = h.entrySet();
Iterator<NameInit> itr = s.iterator();
while(itr.hasNext())
{
Map.Entry<NameInit,PlaceAndAddInit> me = (Map.Entry) itr.next();
NameInit key1 =me.getKey();
if(key.equals(key1)){// this never happens with this code as key and key1 holds different hashcodes. So how do I achieve the equality.
System.out.println(me.getValue());
}
}
}
}
Edit: I tried to obtain equality by equals method to which I discovered that hashcodes of key1 and key are different. Understanding the reason behind this is the purpose of my question.
You're not overriding hashCode() so the default is used. In the default implementation, key and key1 will have different hashCode values and they will not be equal even if you think they should be. So the solution is to override the hashCode and equals method if you want to be able to compare those objects.
To answer your question:
Edit: I tried to obtain equality by equals method to which I
discovered that hashcodes of key1 and key are different. Understanding
the reason behind this is the purpose of my question.
If you don't provide overrides to equals and hashCode, they will get inherited from Object. And here's how they look for Object:
public boolean equals(Object obj) {
return (this == obj);
}
Therefore, 2 objects will only be equal when they are == which means that they point to precisely the same memory location. In your example, it is not the case. hashCode is native so can't show you the source code.
Here's more to read:
Google search about hashCode and equals
Objects.equals is implemented like this:
return (a == b) || (a != null && a.equals(b));
You see, it is basically calling a's equals method, not the hashcode method. No matter how hashcode is implemented Objects.equals returns false when a.equals(b) returns false. It has nothing to do with the hashcode method.
So to fix this, simply override the equals method. This is a simple implementation:
#Override
public boolean equals(Object obj) {
return this.hashcode() == obj.hashcode();
}
Also, if you want to find the value of a key in the hash map, call the get method on the hash map and it will do it for you in O(1) time. No need for such an inefficient approach with O(n) time.
public class DemoHashSet {
private String name;
private String dob;
private String gender;
public DemoHashSet(String name, String dob, String gender) {
super();
this.name = name;
this.dob = dob;
this.gender = gender;
}
public String getName() {
return name;
}
public String getDob() {
return dob;
}
public String getGender() {
return gender;
}
#Override
public String toString() {
return name+" "+dob+" "+gender;
}
#Override
public boolean equals(Object o) {
return this.name.equals(((DemoHashSet)o).getName());
}
#Override
public int hashCode() {
int code = this.name.hashCode() + this.dob.hashCode() + gender.hashCode();
System.out.println("Hash Code: "+code);
return code;
}
public static void main(String args[]) throws ParseException {
Map<DemoHashSet, String> hmap = new HashMap<DemoHashSet, String>();
DemoHashSet obj1 = new DemoHashSet("key1", "121990", "male");
DemoHashSet obj2 = new DemoHashSet("key2", "122990", "male");
DemoHashSet obj3 = new DemoHashSet("key3", "123990", "male");
DemoHashSet obj4 = new DemoHashSet("key4", "124990", "male");
DemoHashSet obj5 = new DemoHashSet("key5", "125990", "male");
hmap.put(obj1, "value1");
hmap.put(obj2, "value2");
hmap.put(obj3, "value3");
hmap.put(obj4, "value4");
hmap.put(obj5, "value5");
System.out.println("Get values: ");
System.out.println(hmap.get(new DemoHashSet("key1", "121990", "male")));
System.out.println(hmap.get(new DemoHashSet("key2", "122990", "male")));
}
}
In this code I am overriding hashcode() and equals() function
when I run this code i get following output:
Hash Code: 1457153183
Hash Code: 1457182975
Hash Code: 1457212767
Hash Code: 1457242559
Hash Code: 1457272351
Get values:
Hash Code: 1457153183
value1
Hash Code: 1457182975
value2
when I override only hashCode() function and comment equlas() method I get following output:
Hash Code: 1457153183
Hash Code: 1457182975
Hash Code: 1457212767
Hash Code: 1457242559
Hash Code: 1457272351
Get values:
Hash Code: 1457153183
null
Hash Code: 1457182975
null
Need explanation for this behaviour as the hash code computed in both scenarios is same but it gives null in 2nd case.
Need explanation for this behavior
This is the exact expected behavior i should expect. why?
There is one thing about hascode and that is you can't give 100% assurance of uniqueness for unique object. That means in some cases
different object can have similar hascode.
And HashMap implementation kept that in mind. so when you pass a key they always compare the hascode and then check equality (reference check then the equals() method ). if nothing found return null. you can check the source code:
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
This code block says:
hash (hascode) need to be same for both given key and entry key
AND
given key and entry key need to be same object (same reference ) OR they need to be equal by the equals() method
In you case when you comment the equals method the default implementation of equals() method form the super class is used, which actually check the reference equality only I guess. so
DemoHashSet obj1 = new DemoHashSet("key1", "121990", "male");
and later
new DemoHashSet("key1", "121990", "male") //ins hmap.get()
doesn't refer to the same object so equals() return false and get() return null as no match found for the key.
When your equals() method exist according to your implementation you are telling those object are equal as they have same name.
hashcode cannot assure and is not Equality.
Need explanation for this behaviour as the hash code computed in both
scenarios is same but it gives null in 2nd case.
It all related to how hashmap's get methods works internally. When you call get method of a hashmap for a specified key, Internally this key's hashcode method gets called to calculate bucket location. Once it gets bucket location, it calls equals method of specified key to compare with existing objects inside that bucket (actually class Entry objects) and if any object is equal to specified object according to equals method, it returns that object.
case 1 : I am overriding hashcode() and equals() methods
hmap.get(new DemoHashSet("key1", "121990", "male"))
hmap.get(new DemoHashSet("key2", "122990", "male"))
according to your equals method implementation, two DemoHashSet objects are equal if they have same name. Thats why you are getting results in this case.
case 2 : I override only hashCode() function and comment equlas() method
hmap.get(new DemoHashSet("key1", "121990", "male"))
hmap.get(new DemoHashSet("key2", "122990", "male"))
In this case, no custom equals method implementation available, so it will take default implementation and according to default implementation - two objects are same if and only if their references are same. So new DemoHashSet("key1", "121990", "male") and new DemoHashSet("key1", "121990", "male") are not same even they are carrying same values. So hashmap is not getting those objects in that bucket. That's why you are getting null.
For more info: http://javarevisited.blogspot.in/2011/02/how-hashmap-works-in-java.html
You are using a HashMap. Internally, HashMap's have a number of buckets, each of which has a list of the items you are storing.
Whenever you call HashMap.get(), the HashMap will call DemoHashSet.hashCode() and use the key argument, possibly with the modulus (%) operator, to figure out which bucket to look in.
Then, it will search through this list using the DemoHashSet.equals() method to see if it can match the key argument. If it equals() ever returns true, then the value will be returned, otherwise null.
From the documentation for Object.equals():
Note that it is generally necessary to override the hashCode method
whenever this method is overridden, so as to maintain the general
contract for the hashCode method, which states that equal objects must
have equal hash codes.
hadCode() function has meant for storing the object in a specific segment/bucket by deriving the key of object to specific segment/bucket. To understand the purpose of this method, you need to know how HashTable or HashMap works internally. Wikipedia is the best source to understand about it.
equals() function has meant for checking the existence of particular Object in the collection object.
If two objects are equal, the hasCode will be same but converse is not true. Two different objects can have same hashCode
I have a problem when retrieving values from a hashmap. The hashmap is declared as follows:
HashMap<TRpair,A> aTable = new HashMap<TRpair,A>();
I then put 112 values into the map as follows:
aTable.put(new TRpair(new T(<value>),new Integer(<value>)),new Ai());
where Ai is any one of 4 subclasses that extend A.
I then proceed to check what values are in the map, as follows:
int i = 0;
for (Map.Entry<TRpair,A> entry : aTable.entrySet()) {
System.out.println(entry.getKey().toString() + " " + entry.getValue().toString());
System.out.println(entry.getKey().equals(new TRpair(new T("!"),new Integer(10))));
i++;
}
i holds the value 112 at the end, as one would expect and the equality test prints true for exactly one entry, as expected.
However, when I do
System.out.println(aTable.get(new TRpair(new T("!"), new Integer(10))));
null is output, despite the above code snippet confirming that there is indeed one entry in the map with exactly this key.
If it helps, the class TRpair is declared as follows:
public class TRpair {
private final T t;
private final Integer r;
protected TRpair(Integer r1, T t1) {
terminal = t1;
row = r1;
}
protected TRpair(T t1, Integer r1) {
t = t1;
r = r1;
}
#Override
public boolean equals(Object o) {
TRpair p = (TRpair)o;
return (p.t.equals(t)) && (p.r.equals(r));
}
#Override
public String toString() {
StringBuilder sbldr = new StringBuilder();
sbldr.append("(");
sbldr.append(t.toString());
sbldr.append(",");
sbldr.append(r.toString());
sbldr.append(")");
return sbldr.toString();
}
}
the equals() and toString() methods in each of the Ai (extending A) and in the T class are overridden similarly and appear to behave as expected.
Why is the value output from the hashmap aTable null, when previously it has been confirmed that the value for the corresponding key is indeed in the map?
With many thanks,
Froskoy.
The keys/elements for a Hash collection but override hashCode() if euqals is overridden.
You could use.
public int hashCode() {
return t.hashCode() * 31 ^ r.hashCode();
}
BTW: It appears from your code that Integer r cannot be null in which case using int r makes more sense.
From Object.equals()
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
IIRC hashmap looks up by hashCode() and not by equality, and since you did not implemented hashcode you use default implementation which is consistent with object pointer equality -
you need to implement proper hashcode function which takes into account "T" parameter as well as integer (or not)
It is good practice that hashCode() and equals() are consistent, but not structly necessary if you know what you are doing.