hascode and equals methods not overridden - How th

2019-08-14 03:58发布

I have a class Student and Marks. I am using Student Object as Key for HashMap and Marks as Value. If I don't override hashMap and equals, It still works fine.

i. Can someone please explain how does it internally works on it if not overriding both equals() and hashcode() ii. what If I override only hashcode()

iii.what If I override only equals()

class Student {

    String name;
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    String lastName;

    Student(String name, String lastName){
        this.name = name;
        this.lastName = lastName;
    }
    public String toString(){
        return(" Name : " + this.getName() + " Last Name : " + this.getLastName());
    }
}
class Marks {
    Student s;
    String marks;
    public Student getS() {
        return s;
    }
    public void setS(Student s) {
        this.s = s;
    }
    public String getMarks() {
        return marks;
    }
    public void setMarks(String marks) {
        this.marks = marks;
    }
    Marks (Student s, String marks){
        this.marks = marks;
        this.s = s;
    }
    public String toString(){
        return(" Marks : " + this.getMarks());
    }
}



public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("Vishnu","Verma");
        Student s2 = new Student("Amit","Sharma");

        Marks m1 = new Marks(s1,"65%");
        Marks m2 = new Marks(s2,"67%");

        Map <Student,Marks>map = new HashMap<Student,Marks>();
        map.put(s1, m1);
        map.put(s2, m2);

        System.out.println(map);
    }
}

4条回答
我只想做你的唯一
2楼-- · 2019-08-14 04:07

If you don't override equals and hashcode, the implementation is inherited from Object.

The method hashcode() is used by a HashMap to determine in a which sublist the entry will be in. So if you don't override (or even worse, override and give back the same value for each instance) the method, HashMap might store every entry in the same sublist. This will make the map slower, but putting in values will still work.

For your last question see https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--

Long story short: You shouldn't override just one. You should override both.

查看更多
姐就是有狂的资本
3楼-- · 2019-08-14 04:13

It will consider the objects equal if their references are equal. i.e. they point to the same object.

查看更多
放我归山
4楼-- · 2019-08-14 04:14

Let us review how a HashMap works (in a simplistic case).

First we create our hash table:

  1. we create a number of buckets - lets say 2
  2. we then decide which hashcodes go in which buckets, lets say negative in one and positive (and zero) in the other.

When we add a key -> value mapping to our HashMap we:

  1. calculate the hashCode of the key
  2. given the hashCode and the above hash table we select a bucket
  3. we determine if there is already something in that bucket, if there is we use a simple data structure (say a singly linked list) to store multiple key -> value mappings in the same bucket.

To get a value by key from our HashMap we:

  1. calculate the hashCode of the key
  2. given the hashCode and the above hash table we select a bucket
  3. if there is a single item in the bucket we check whether they key of the mapping equals to the requested key, if so we return the value
  4. if there is more than one item in the bucket we loop over the items in the bucket to find the first one where the key equals the requested key and return its value
  5. if no such mapping exists we return null.

So, to answer your questions:

how does it internally works on it if not overriding both equals() and hashcode()?

I assume you mean if you do not override anything and leave it to the default behaviour.

In this case the HashMap uses Object.hashCode and Object.equals; the HashMap will behave exactly like an IdentityHashMap - i.e. it will use System.identityHashCode to calculate the hashCode and it will use == to test for equality.

This default behaviour will only work correctly when dealing with the same instance of a class.

what If I override only hashcode()?

TL;DR: Your HashMap will behave as above.

You hashCode will be used to select a bucket at insertion (2) and during retrieval (2) but the default equals will be used to determine whether the correct key is found.

It is likely that the Map will work similarly to the above case, as your hashCode implementation is unlikely to break the general contract of hashCode, i.e. only the exact same instance will be equal to itself and if hashCode compiles with the fact that it must return the same value when it is invoked multiple times on the same instance it must by extension comply with this requirement.

NB: The converse is not true. Overriding only equals immediately breaks the contract of hashCode due to items that are equals returning different hashcodes. This will break any hash based collection from HashMap to HashSet because you will have duplicate keys and the collection will exhibit Undefined Behaviour.

This issue will be that there are many more objects that you will return the same hashCode for than objects that you consider equals - whilst this is part of the very nature of hashing, you will compound this problem.

It is likely that your HashMap will be inefficient in this case.


To illustrate what I mean consider this simple example of a class with one element:

class IntHolder {
    int value;

    //getter setter
}

If we use the default equals and hashCode then the our Map would exhibit the following behaviour:

final Map<IntHolder, String> map = new HashMap<>();
final IntHolder a = new IntHolder();
a.setValue(7);
final IntHolder b = a;
final IntHolder c = new IntHolder();
c.setValue(7);

map.put(a, "A");
System.out.println(map.get(a)); // --> A
System.out.println(map.get(b)); // --> A
System.out.println(map.get(c)); // --> null

map.put(c, "C");
System.out.println(map.get(a)); // --> A
System.out.println(map.get(b)); // --> A
System.out.println(map.get(c)); // --> C

i.e. the Map will only consider two keys that are == (such as a and b) to be the same.

If you ever "lose" a reference to a key instance you will never be able to get it back. So if you did:

c = null;

You would never, without looping over the Map, be able to get "C" out of the Map again.

OP asks: And How we determine only hashCode() need to be overridden or only equals() need to be overridden?

You should only every override both or neither of these methods.

There is never a use case for overriding only equals or only hashCode.

查看更多
男人必须洒脱
5楼-- · 2019-08-14 04:17

If not implemented in your class, then hashCode and equals fallback to the Object implementations which are:

public native int hashCode();

public boolean equals(Object obj) {
    return (this == obj);    
}

So the implementation of hashCode varies depending on the runtime environment but more interestingly regarding HashMap, the equals methods require the two objects to be the same instance to be equal!

Object cannot guess for you what makes two instances equal so it defaults to the strongest constraint.

查看更多
登录 后发表回答