0
votes

I have some dilemma about hashcodes. As I know Object class hashcode represent the memory location of any object and for every object(distinct objects) this hashcode is unique. Ok, this point is very much straight forward.

But in case of hashing(HashSet, HashMap etc), we need to override hashcode and equals method. Now in this case hashcode will represent the bucket location. Now I have few question:

For example I am taking Student class and I override hashcode() method of Student class..

class Student{
   //
   int id;
   @override
   int hashcode(){
      return id*31;
   }
}

Student stu1 = new Student(1);
Student stu2 = new Student(1);

If I create two same Student objects(stu1, stu2) which are returning similar hashcode. So if we add these two objects in hashset, it will call it's hashcode and get the bucket location but what will happen with real student(two similar objects that we created) object? Means what will be it's memory location in heap? Are both pointing to same heap location?

4
"As we all know Object class hashcode represent the memory location of any object and for every object" - That's not actually true, by default hashcode may return the a hash representing the memory location, but it's quite legal to return whatever you want, so long as you maintain the contract between equals and hashcodeMadProgrammer
"Are both pointing to same heap location?" - No. What will happen is the first object will be bumped and replaced by the second, so the bucket now only contains the second object reference...MadProgrammer
@MichaelGoldstein So if we override hascode then Is there any way to get object's memory location?JavaLearner
@MadProgrammer first object will be bumped and replaced by the second I believe both objects will be on the heap. Having same hash code does not mean they refer to same object on garbage-collectible heap. And in a hashed collection, it means in the same bucket. I don't see any replacement happening.Meena Chaudhary
@MeenaChaudhary He is talking about the replacement in bucket not in heap.JavaLearner

4 Answers

0
votes

HashCode never determines the location in heap. Both equals and hashcode is used to compare equality of objects, primarily inside hash based collections such as Hashtable and HashMap.

(There is absolutely no way to find out the memory location.)

0
votes

As you know Set implements equals() and hashcode() method,

  1. So if the hashcode differs for two object , then there will be two elements in the set
  2. If Hashcode is equal , the second entry replaces the first one . so there will be only one element in the set

When two unequal objects have the same hash value, this causes a collision in the hash table, because both objects want to be in the same slot (bucket). The hash algorithm must resolve such collisions.

Student stu1 = new Student(1);
Student stu2 = new Student(1);

When you add stu1 and stu2 into the set with same value into the set,

Set<Student> sset=new HashSet<Student >();    
sset.add(stu1);
sset.add(stu2);

the set sset will hold two values with the same values , but both refer to two different objects as their hashcode is different

0
votes

Hmm...Its not too hard to see whether they share the same memory location. A reference equality test, i.e stu1==stu2 will answer that. As for the hash, they will fall in the same bucket because they have the same hashcode.So, given that condition, when you try to retrieve the student, the equals method will be used to determine which Student is being looked for. Run the code below.

import java.util.HashSet;

class Student {
    //
    int id;

    Student(int id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id * 31;
    }

    public static void main(String[] args) {
        Student stu1 = new Student(1);
        Student stu2 = new Student(1);
        Student stu3 = stu1;
        HashSet<Student> hash = new HashSet<Student>();
        hash.add(stu1);
        hash.add(stu2);

        if(stu1==stu2){
            System.out.println("Stu1 and stu2 refer to the same 
location in memory...");
        }

        if(stu1==stu3){
            System.out.println("Stu1 and stu3 refer to the same 
location in memory...");
        }

    }

}
0
votes

Object's Hashcode is not a physical memory location of the object located in heap. Its a digest of the data stored in an instance of the class into a single hash value (a 32-bit signed integer). (Source wiki)

But in case of hashing(HashSet, HashMap etc), we need to override hashcode and equals method

Yes, until and unless you're using your class instance as a key in any map implemented classes.

Now consider your given scenrio :

@override
int hashcode(){
  return id*31;
}

Nothing will happen but you end up adding different objects in to your map or set.

Important Contract :

There is a contract between object hashcode and equals method saying that, when you override equals you must override hascode() and vice versa inorder to uniformly distribute the object among the buckets.

Be Carefull !!

// The worst possible legal hash function - never use!

@Override 
public int hashCode() { 
return 42; 
}

(Source Effective Java)

It’s very much legal because it ensures that equal objects have the same hash code. It’s atrocious because it ensures that every object has the same hash code. Therefore, every object hashes to the same bucket, and hash tables degenerate to linked lists. Programs that should run in linear time instead run in quadratic time. For large hash tables, this is the difference between working and not working.

For your clarification :

Consider this, Bellow is my class and here I'm defining the contract between equals and hascode saying that two objects having same roll and name must be identical.

public class MyClass {
  private int roll;
  private String name;
  private String subject;

public MyClass(int roll, String name, String subject) {
    this.roll = roll;
    this.name = name;
    this.subject = subject;
}

@Override
public boolean equals(Object object) {
    boolean result = false;
    if (object == null || object.getClass() != getClass()) {
        result = false;
    } else {
        MyClass myclass = (MyClass) object;
        if (this.roll == myclass.getRoll()
                && this.name == myclass.getName()) {
            result = true;
        }
    }
    return result;
}

@Override
public int hashCode() {
    int hash = 17;
    hash = 31 * this.roll;
    hash = 31 * hash + this.name.hashCode();
    return hash;
}

public static void main(String args[]) {
    MyClass obj1 = new MyClass(1, "A", "Math");
    MyClass obj2 = new MyClass(1, "A", "Phy");
    MyClass obj3 = new MyClass(1, "B", "Chem");
    System.out.println("obj1.equals(obj2) : "+obj1.equals(obj2));  // true. As both the objects have the same roll & name
    System.out.println("obj1 == obj2 : "+(obj1 == obj2));   // false. because two are references of different instance.
    Set<MyClass> set = new HashSet<MyClass>();
    set.add(obj1);
    set.add(obj2);
    System.out.println("set :"+set.size()); // 1 object 
    for(MyClass cls:set){
    System.out.println(cls); //i.e [1 A Math]. It'll not replaced by the 2nd one.
    }
    Map<MyClass,String> map= new HashMap<MyClass,String>();
    map.put(obj1,"IN");
    map.put(obj2,"US");
    System.out.println("map :"+map.size());// 1 object

    for (Map.Entry<MyClass, String> entry : map.entrySet()){
        System.out.println(entry.getKey() + " : " + entry.getValue());   // [1 A Math : US]. here you may notice the key remains same but the value will be replaced.
    }

}

public int getRoll() {
    return roll;
}

public String getName() {
    return name;
}

public String getSubject() {
    return subject;
}
@Override
public String toString(){
    return ""+roll+" "+name+" "+subject;
}

}

Note: Whenever you're creating an object using new keyword, each time it'll create a different object in the heap no matter the contents in them are same or not.