问题
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);
}
}
回答1:
It will consider the objects equal if their references are equal. i.e. they point to the same object.
回答2:
Let us review how a HashMap
works (in a simplistic case).
First we create our hash table:
- we create a number of buckets - lets say 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:
- calculate the
hashCode
of the key - given the
hashCode
and the above hash table we select a bucket - 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:
- calculate the
hashCode
of the key - given the
hashCode
and the above hash table we select a bucket - 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 - 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 - 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
.
回答3:
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.
回答4:
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.
来源:https://stackoverflow.com/questions/32376875/hascode-and-equals-methods-not-overridden-how-the-put-and-get-will-work