散列表(Hash Map)
今天第一次做Leetcode用到了散列表,之前學的數據結構的內容都忘了,正好趁熱打鐵補一補。
摘自其他博客的一個整合、
一、哈希表簡介
數據結構的物理存儲結構只有兩種:順序存儲結構和鏈式存儲結構(像棧,隊列,樹,圖等是從邏輯結構去抽象的,映射到內存中,也這兩種物理組織形式),在數組中根據下標查找某個元素,一次定位就可以達到,哈希表利用了這種特性,哈希表的主幹就是數組。
比如我們要新增或查找某個元素,我們通過把當前元素的關鍵字 通過某個函數映射到數組中的某個位置,通過數組下標一次定位就可完成操作。
存儲位置 = f(關鍵字)
另一種解釋如下:
哈希表就是一種以 鍵-值(key-indexed) 存儲數據的結構,我們只要輸入待查找的值即key,即可查找到其對應的值。
哈希的思路很簡單,如果所有的鍵都是整數,那麽就可以使用一個簡單的無序數組來實現:將鍵作為索引,值即為其對應的值,這樣就可以快速訪問任意鍵的值。這是對於簡單的鍵的情況,我們將其擴展到可以處理更加復雜的類型的鍵。
使用哈希查找有兩個步驟:
- 使用哈希函數將被查找的鍵轉換為數組的索引。在理想的情況下,不同的鍵會被轉換為不同的索引值,但是在有些情況下我們需要處理多個鍵被哈希到同一個索引值的情況。所以哈希查找的第二個步驟就是處理沖突
- 處理哈希碰撞沖突。有很多處理哈希碰撞沖突的方法,本文後面會介紹拉鏈法和線性探測法。
二、JAVA中 HashMap類的方法介紹
此處默認HashMap<Key Value>
1、clear()
clear() 的作用是清空HashMap。它是通過將所有的元素設為null來實現的。
public void clear() { modCount++; Entry[] tab = table; for (int i = 0; i < tab.length; i++) tab[i] = null; size = 0; }
2、containsKey()
containsKey() 的作用是判斷HashMap是否包含key。
public boolean containsKey(Object key) { return getEntry(key) != null; }
containsKey() 首先通過getEntry(key)獲取key對應的Entry,然後判斷該Entry是否為null。
3、containsValue()
containsValue() 的作用是判斷HashMap是否包含“值為value”的元素。
public boolean containsValue(Object value) { // 若“value為null”,則調用containsNullValue()查找 if (value == null) return containsNullValue(); // 若“value不為null”,則查找HashMap中是否有值為value的節點。 Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (value.equals(e.value)) return true; return false; }
從中,我們可以看出containsNullValue()分為兩步進行處理:第一,若“value為null”,則調用containsNullValue()。第二,若“value不為null”,則查找HashMap中是否有值為value的節點。
containsNullValue() 的作用判斷HashMap中是否包含“值為null”的元素。
4、entrySet()、values()、keySet()
它們3個的原理類似,這裏以entrySet()為例來說明。
entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一個集合。
// 返回“HashMap的Entry集合” public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } // 返回“HashMap的Entry集合”,它實際是返回一個EntrySet對象 private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } // EntrySet對應的集合 // EntrySet繼承於AbstractSet,說明該集合中沒有重復的EntrySet。 private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>) o; Entry<K,V> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o) != null; } public int size() { return size; } public void clear() { HashMap.this.clear(); } }
5、get()
get() 的作用是獲取key對應的value,它的實現代碼如下:
public V get(Object key) { if (key == null) return getForNullKey(); // 獲取key的hash值 int hash = hash(key.hashCode()); // 在“該hash值對應的鏈表”上查找“鍵值等於key”的元素 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
6、put()
put() 的作用是對外提供接口,讓HashMap對象可以通過put()將“key-value”添加到HashMap中。
public V put(K key, V value) { // 若“key為null”,則將該鍵值對添加到table[0]中。 if (key == null) return putForNullKey(value); // 若“key不為null”,則計算該key的哈希值,然後將其添加到該哈希值對應的鏈表中。 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 若“該key”對應的鍵值對已經存在,則用新的value取代舊的value。然後退出! if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 若“該key”對應的鍵值對不存在,則將“key-value”添加到table中 modCount++; addEntry(hash, key, value, i); return null; }
散列表(Hash Map)