This question already has answers here:
Java HashMap return value not confirming with my understanding of equals and hashcode
(4 answers)
Closed 8 years ago.
What I know is: While inserting elements in the HashMap, Java checks value of hashCode and inserts that element in inside the HashMap and while retrieving the object from HashMap, Java checks the value of HashCode and retrieved the object that has the value generated from that HashCode. Is this correct?
I created a modal to override default implementation of HashCode. Every time that modal is called, it gives back a same value. So, if we add that modal again and again, why entries in HashMap are increasing?
Here is my code:
Modal:
public class MyModal {
int empId;
String empName;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyModal myModal = (MyModal) o;
if (empId != myModal.empId) return false;
if (empName != null ? !empName.equals(myModal.empName) : myModal.empName != null) return false;
return true;
}
#Override
public int hashCode() {
return 1;
}
public MyModal(int empId, String empName) {
this.empId = empId;
this.empName = empName;
}
}
public class TestHashCode {
public static void main(String[] args) {
HashMap<MyModal, Integer> hashMap = new HashMap<>();
MyModal modal1 = new MyModal(1, "a");
MyModal modal2 = new MyModal(2, "b");
hashMap.put(modal1, 1);
hashMap.put(modal2, 2);
System.out.println("Size is" + hashMap.size());
System.out.println(modal1.hashCode() + " "+modal2.hashCode());
}
}
Output:
Size is2
1 1
Hash collisions aren't the only limiting factor in the map construction, however returning a constant 1 is a "worst" case Map and it behaves like a LinkedList (every element is in one bucket). From the Object.hashCode() Javadoc
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.
Both HashCode and Equals need to be implemented. Hash code is used to narrow the search in equals need to be implemented to define equality. Two unequal objects can have same hashcode but does not mean they are equal.
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode().
Related
This question already has answers here:
Understanding the workings of equals and hashCode in a HashMap
(9 answers)
Closed 2 years ago.
I have a Test class and its objects with same value (101,"abc") is created twice. I am inserting these two object in Hashmap as a Key. I want to understand the internal functioning as to why I am getting size as two of a map when both of my keys are same, it should probably overwrite ?
import java.util.HashMap;
import java.util.Map;
public class Test{
int id;
String name;
public static void main(String[] args) {
Test e1 = new Test(101,"abc");
Test e2 = new Test(101,"abc");
//Test e3 = new Test(101,"abc");
Map<Test, String> map = new HashMap<>();
map.put(e1, "XYZ");
map.put(e2, "CDF");
String value = map.get(e2);
System.out.println( "VALUE : "+value);
}
public Test(int id, String name) {
this.id = id;
this.name=name;
}}
Test e1 = new Test(101,"abc");
Test e2 = new Test(101,"abc");
Creates 2 different objects of type Test. This means 2 different memory space has been allocated for e1 & e2.
Now lets understand how does map( say Hash Map) identifies the uniqueness of key( in much simpler words, how does the map knows that the key you are trying to insert is already present or not). Answer is, map calls the hashcode() & equals() method to compare the keys present in map with the one you are trying to insert.
As we already know all classes have a default parent class Object . And the Test class is not having an implementation of hashcode() & equals(); so when map tried calling them, the object class method were called.
As Object's class equals returns true only when both the objects in comparison refers to the same object reference & so does the hashcode() and here e1 & e2 are clearly different objects. So you got two entries.
Solution is to have an override the equals() & hashcode() as suggested by #Kapil above.
There will be two objects in the hashmap unless you override hashcode() and equals() methods in your Test class. If you override these two methods on the id property of Test class then the two keys with the same id would be treated as duplicate and you will see the expected behaviour.
Example below states that you want to make two object equals if their ids are equal -
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Test other = (Test) obj;
if (id != other.id)
return false;
return true;
}
import java.util.HashMap;
import java.util.Map;
class Geek
{
public String name;
public int id;
Geek(String name, int id)
{
this.name = name;
this.id = id;
}
#Override
public boolean equals(Object obj)
{
// checking if both the object references are
// referring to the same object.
if(this == obj)
return true;
// it checks if the argument is of the
// type Geek by comparing the classes
// of the passed argument and this object.
// if(!(obj instanceof Geek)) return false; ---> avoid.
if(obj == null || obj.getClass()!= this.getClass())
return false;
// type casting of the argument.
Geek geek = (Geek) obj;
// comparing the state of argument with
// the state of 'this' Object.
System.out.println("equals method ....."+(geek.name == this.name && geek.id == this.id));
return (geek.name == this.name && geek.id == this.id);
}
int counter = 0;
#Override
public int hashCode()
{
// We are returning the Geek_id
// as a hashcode value.
// we can also return some
// other calculated value or may
// be memory address of the
// Object on which it is invoked.
// it depends on how you implement
// hashCode() method.
++counter;
System.out.println("counter ::>>> "+counter);
return counter;
}
Driver code:
public static void main (String[] args)
{
Map<Geek, Integer> map = new HashMap<>();
// creating the Objects of Geek class.
Geek g1 = new Geek("aa", 1);
Geek g2 = new Geek("aa", 1);
map.put(g1, g1.id);
map.put(g2, g2.id);
map.forEach((k,v) -> {
System.out.println("key = "+k + "\n value = "+v);
});
/* else
System.out.println("Both Objects are not equal. "); */
}
Here, I am overriding the hashCode() method but still the map contains only one object which is g2. Why didn't the HashMap store two objects, given that my hashcode returns a different integer every time?
Even though my equals() method returns true for the same object, why is the HashMap not storing two objects? Can someone please guide me in this regard?
Your counter variable is an instance variable, so it's initialized to 0 for each Geek instance. Therefore, both g1 and g2 have the same hashCode() of 1 when you put them in the Map, and are considered identical by the HashMap, since they are equal to each other based on your equals implementation.
If you change counter to be static, you will get different hashCode() for the 2 instances of Geek, and they would be stored in separate map entries.
That said, your hashCode() implementation is very bad. If you call hashCode() for the same instance multiple times, you'll get a different result each time! This means that if you attempt to put g1 twice in the Map, it will probably put it twice, since the second put will see a different hashCode(), and will therefore search for the key in a different bucket.
hashCode() function must not change when you call it on the same object instance multiple times. You can't generate a new value each time you call it, right now you are doing it by incrementing counter.
As per Object.hashCode() javadoc:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
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.
This question already has answers here:
Search an ArrayList for a particular Object
(6 answers)
Closed 9 years ago.
I have an ArrayList that is defined as follows:
ArrayList<Doctor> arr = new ArrayList<Doctor>();
The Doctor class contains the following:
String name;
String gender;
String speciality;
In the arr arraylist i have added 100 Doctor objects.
Now i need to search the arr ArrayList to see if a particular doctor object is present.
I tried the following approach;
boolean contains = maarrp.containsKey(doc.name);
But, i don't want to compare using keys or an element of the Doctor object. Instead i want to compare the whole doctor object. How can i do this?
You need to implement the equals() and hashcode() methods in you Doctor Object then Search against ArrayList like below.
ArrayList<Doctor> arr = new ArrayList<Doctor>();
arr.add(new Doctor());
arr.add(new Doctor());
arr.add(new Doctor());
arr.add(new Doctor());
if(arr.contains(doc){
}
Create your Doctor class like below
class Doctor{
Long id;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Doctor other = (Doctor) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
Override equals() and hashcode() methods in Doctor class.
your class should be something like :
class Doctor
{
String name;
String gender;
String speciality;
public boolean equals()
{
//some logic on which you want to say
//two doctors are same.
return true;
}
public int hashCode()
{
return 0;
}
}
for hashCode you must follow these rules :
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
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.
Note : hashCode must not always return 0 :)
Override equals() and hashcode() methods in Doctor Class
/*add a unique id field in doctor class to make each doctor unique.*/
String docUniqueId;
String name;
String gender;
String speciality;
#Override
public boolean equals(Object ob) {
if (!ob instanceof Doctor ) return false;
Doctor that = (Doctor )ob;
return this.docUniqueId== that.docUniqueId;
}
#Override
public int hashCode() {
return docUniqueId;
}
You could implement the equals() and hashcode() methods of your Doctor class. These methods are called when the contains() method of the ArrayList object is called.
To simplify this work, you can eventually use the commons-lang library which provides:
EqualsBuilder (http://commons.apache.org/proper/commons-lang/apidocs/index.html?org/apache/commons/lang3/builder/ToStringBuilder.html)
and HashCodeBuilder (http://commons.apache.org/proper/commons-lang/apidocs/index.html?org/apache/commons/lang3/builder/HashCodeBuilder.html)