首先拋出答案:這和集合的某些操做有關(好比HashSet的add方法)java
HashSet add(E e) 方法以下:數組
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
複製代碼
在HashSet的內部維護了一個HashMap,能夠看到對於HashSet的add操做委託給了HashMap的put 操做。bash
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
//內部維護的HashMap
private transient HashMap<E,Object> map;
複製代碼
繼續查看HashMap的put方法以下:app
public V put(K key, V 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;
return oldValue;
}
}
addEntry(hash, key, value, i);
return null;
}
複製代碼
咱們大體分析一下這段代碼邏輯:ui
咱們再來看看HashMap的hash方法以下:spa
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
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);
}
複製代碼
咱們能夠看到h ^= k.hashCode();
這個調用,這也正好是調用了元素的hashCode方法。code
重點來了:若是咱們不重寫hashCode方法會產生什麼樣的結果?對象
咱們回頭看看HashMap的put方法,若是這個時候重複添加具備相同內容的對象,若是不重寫hashCode,那麼這倆個對象就有可能會在Entry數組的不一樣位置分別構建entry,然而它們的內容倒是一致的,即便你重寫了equals方法並不必定能保證HashSet中的元素惟一。接口
因此,對於集合操做好比Set類型接口,Map類型接口而言,重寫了equals方法必定要重寫hashCode 保證元素惟一性。ip
而對於equals使用的準則有:
自反性:對於任何非空引用值 x,x.equals(x) 都應返回 true。
對稱性:對於任何非空引用值 x 和 y,當且僅當 y.equals(x) 返回 true 時,x.equals(y) 才應返回 true。
傳遞性:對於任何非空引用值 x、y 和 z,若是 x.equals(y) 返回 true, 而且 y.equals(z) 返回 true,那麼 x.equals(z) 應返回 true。
一致性:對於任何非空引用值 x 和 y,屢次調用 x.equals(y) 始終返回 true 或始終返回 false, 前提是對象上 equals 比較中所用的信息沒有被修改。
非空性:對於任何非空引用值 x,x.equals(null) 都應返回 false。