从学习Java开始,就从各个师兄、各种书籍、各大网站听到、看到,重写equals方法必须重写hashCode方法。重写equals方法必须重写hashCode方法。重写equals方法必须重写hashCode方法。
那么为什么呢?今天就详细剖析一下equals和hashCode!
equals方法是比较两个对象实例是否相等。Object中equals方法的描述为:
public boolean equals(Object obj) {
return (this == obj);
}
也就是默认情况下,equals是比较两个对象的引用是否相等,这个情况下除非两个对象引用指向同一个对象,否则都会返回false。而实际场景中,我们并不需要比较两个对象的地址,而是比较它们的内同通过是否相等,这就需要我们重写equals方法。
那么是不是就简单的比较下内容是否相等呢?有什么需要注意的吗?
看下Java源码的描述:
思考这样的问题:
1. 假设两个对象的内容一样,那么它们调用equals方法会相等吗?
2. 如果在状态不改变的情况下,两次比较的结果不一样,你会不会抓狂?
3. 什么情况下返回true,什么情况下又返回false?
让我们一个一个的看,如果两个对象内容相同,但类型不一样,它们还equals吗?
/**
* Indicates whether some other object is "equal to" this one.
* <p>
* The {@code equals} method implements an equivalence relation
* on non-null object references:
* <ul>
* <li>It is <i>reflexive</i>: for any non-null reference value
* {@code x}, {@code x.equals(x)} should return
* {@code true}.
* <li>It is <i>symmetric</i>: for any non-null reference values
* {@code x} and {@code y}, {@code x.equals(y)}
* should return {@code true} if and only if
* {@code y.equals(x)} returns {@code true}.
* <li>It is <i>transitive</i>: for any non-null reference values
* {@code x}, {@code y}, and {@code z}, if
* {@code x.equals(y)} returns {@code true} and
* {@code y.equals(z)} returns {@code true}, then
* {@code x.equals(z)} should return {@code true}.
* <li>It is <i>consistent</i>: for any non-null reference values
* {@code x} and {@code y}, multiple invocations of
* {@code x.equals(y)} consistently return {@code true}
* or consistently return {@code false}, provided no
* information used in {@code equals} comparisons on the
* objects is modified.
* <li>For any non-null reference value {@code x},
* {@code x.equals(null)} should return {@code false}.
* </ul>
* <p>
*/
public boolean equals(Object obj) {
return (this == obj);
}
SUN已经说了,equals需要遵守的规则(一下x,y均不为null):
1. 自反性(x.equals(x) 为true)
2. 一致性(x.equals(y)或y.equals(x)在没有x,y指向的对象没有改变的情况下,多次比较的结果应该是一致且明确的)
3. 传递性(x.equals(y) && y.equals(z)为true ,则x.equals(z)为true)
4. 对称性(x.equals(y) == y.equals(x) 始终为true)
下面上例子(String重写了equals):
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
那么为什么说重写equals,就必须重写hashCode呢?
源码中对hashCode方法的返回值的描述:a hash code value for this object.This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.
这里提到了HashMap,再看HashMap中使用hashCode的地方
final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
从上面看到,HashMap是基于HashTable来存放元素的,在存放K-V时,会将K的hashCode作为hash函数的变量来计算K的位置,如果两个对象equals为真,但hashCode不等,那么它们将被散列到map的不同位置,最后结果就是map中存在两个K equals的对象,这就违反了Map要求Key唯一的约定。
因此,
如果重写equals,请重写hashCode。
如果重写equals,请重写hashCode。
如果重写equals,请重写hashCode。
版权声明:本文为博主原创文章,未经博主允许不得转载。