當在散列集合中放入key時,將自動查看key對象的hashCode值,若此時放置的hashCode值和原來已有的hashCode值相等,則自動調用equals()方法,若此時返回的爲true則表示該key爲相同的key值,只會存在一份。
Object中關於hashCode和equals方法的定義爲:
- public boolean equals(Object obj) {
- return (this == obj);
- }
- public native int hashCode();
基類的hashCode是一個native方法,訪問操作系統底層,它得到的值是與這個對象在內存中的地址有關。
Object的不同子類對於equals和hashCode方法有其自身的實現方式,如Integer和String等。
equals相等的,hashCode必須相等
hashCode不等的,則 equals也必定不等。
hashCode相等的 equals不一定相等(但最好少出現 hashCode相等的情況)。
HashMap的put(K, Value)方法提供了一個根據K的hashCode來計算Hash碼的方法hash()
- transient Entry[] table;
- public V put(K key, V value) {
- if (key == null)
- return putForNullKey(value); //HashMap支持null的key
- int hash = hash(key.hashCode()); //根據key的hashCode計算Hash值
- int i = indexFor(hash, table.length); //搜索指定Hash值在對應table中的索引
- for (Entry<K,V> e = table[i]; e != null; e = e.next) { //在i索引處Entry不爲空,循環遍歷e的下一個元素
- 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;
- }
- }
- //若i索引處Entry爲null,表明此處還沒有Entry
- modCount++;
- addEntry(hash, key, value, i); //將key、value添加到i索引處
- return null;
- }
- static int hash(int h) {
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
對於任意給定的對象,只有它的hashCode()返回值相同,那麼程序調用hash(int h)方法所計算得到的Hash碼值總是相同的。接下來會調用indexFor(int h, int length)方法來計算該對象應該保存在table數組的哪個索引處。
它總是通過h & (table.length - 1)來得到該對象的保存位置--而HashMap底層數組的長度總是2的n次方,這樣就保證了得到的索引值總是位於table數組的索引之內。
當通過key-value放入HashMap時,程序就根據key的hashCode()來覺得Entry的存儲位置:若兩個Entry的key的hashCode()相同那麼他們的存儲位置相同;若兩個Entry的key的equals()方法返回true則新添加Entry的value將覆蓋原有Entry的value,但key不會覆蓋;若兩個Entry的key的equals()方法返回false則新加的Entry與集合中原有的Entry形成Entry鏈。
HashSet的add(E)的實現是通過HashMap的put方法來實現的。(HashSet內部是通過HashMap來實現的,TreeSet則是通過TreeMap來實現的)
根據key的hashCode計算器Hash值,然後取得該Hash值在表table的索引,取得該索引i對應的table中的Entry,判斷key的equals()。