標籤:next 自己的 不同的應用 water iter expect 文章 false lis
Map是索引值對。也是經常使用的資料結構。
Map介面定義了map的基本行為。包含最核心的get和put操作,此介面的定義的方法見:
JDK中有不同的的map實現,分別適用於不同的應用情境。如安全執行緒的hashTable和非安全執行緒的hashMap.
例如以是JDK中map介面的子類UML類圖,當中有個特例Dictionary已經不建議使用:
Map介面中的方法我們須要關注的就是get、put 和迭代器相關的方法如entrySet()、keySet()、values()方法。
Entry
在開始分析map之前,首先瞭解map中元素的儲存。我們知道map能夠覺得是索引值對的集合,java中map使用Entry儲存索引值對,這是一個介面,其定義例如以下,簡單明了。介面方法主要是對鍵和值進行操作。
interface Entry<K,V> { K getKey(); V getValue(); V setValue(V value); boolean equals(Object o); int hashCode(); }
AbstractMap
Map介面的抽象實現。見下面示範範例實現代碼:
Map<String,String> a = /** * *抽象map實現示意,依據文檔說。和list介面及其相似。 * *map分為可變和不可變兩種。不可變僅僅需實現 entrySet方法就可以,且返回的 set的迭代器不能支援改動操作。 * *可變map,須要實現put方法,然後 entrySet的迭代器也須要支援改動操作 * * *AbstractMap 裡面實現了map的梗概,可是其效率難說,比方其get方法中時採用方法entrySet實現的。 * *通常子類用更有效率的方法覆蓋之。
如hashMap中覆蓋了keySet 、values 、get方法等 */ new AbstractMap<String,String>(){ /* * 返回map中的元素集合,返回的集合通常繼承AbstractSet 就可以。 */ @Override public Set<Map.Entry<String, String>> entrySet() { return new AbstractSet<Map.Entry<String,String>>() { @Override public Iterator<java.util.Map.Entry<String, String>> iterator() { return null; } @Override public int size() { return 0; } }; } /* * 預設實現拋出異常,可變map須要實現此方法 */ @Override public String put(String key, String value) { return null; } };
HashMap
hashMap繼承abstractMap,是相當經常使用的資料結構,採用hash散列的思想。能夠在O(1)的時間複雜度插入入和擷取資料。其基本實現能夠分析上個小節中的抽象方法,文章
淺析HashMap的實現和效能分析 已經對hashMap的實現、put和get操作進行了較具體的說明。
這裡不再贅述。關鍵看他的迭代器實現,這裡僅僅分析下entrySet()方法,而keySet()和values()方法實現與之中的一個脈相承。
關於迭代器。見以下摘出的部分原始碼和相關凝視:
/** * 返回map中全部的索引值對集合,用於遍曆 */ public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } /** * 延遲初始化,僅僅有使用的時候才構建。 * * values()和 keySet()方法也使用了相似的機制 */ private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } /** * 真正的 enterySet,是一個內部類,其關鍵實現是迭代器實現。 * * values()和 keySet()方法也相應了相應的內部類。 * 相應的自己的迭代器實現。關鍵在於這個迭代器 * */ 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(); } } /** * entrySet迭代器,繼承HashIterator,實現next方法。 * values()和 keySet()方法,也是繼承HashIterator。僅僅是實現next 的方法不同。 * * 能夠對照下。 * * 關鍵在於HashIterator * * */ private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } /** * *keySet()相應的迭代器 */ private final class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } /** * * hashmap entrySet() keySet() values()的通用迭代器 */ private abstract class HashIterator<E> implements Iterator<E> { Entry<K,V> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot Entry<K,V> current; // current entry HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; //構造時候,在數組中尋找第一個不為null的數組元素,即Entry鏈表,關於hashmap的實現請看 //本人前面的博文 while (index < t.length && (next = t[index++]) == null) ; } } public final boolean hasNext() { return next != null; } /** * 關鍵實現,非常easy看懂。尋找next的時候,和構造迭代器的時候一樣 */ final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); if ((next = e.next) == null) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; } public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } }
HashTable
實現和hashMap基本一致,僅僅是在方法上加上了同步操作。
多線程環境能夠使用它。只是如今有ConcurrentHashMap了,在高並發的時候,能夠用它替換hashtable.
LinkedHashMap
hashMap可能在某些情境下不符合要求,由於放入到當中的元素是無序的。而LinkedHashMap則在一定程度上解決問題。
其在實現上繼承了HashMap,在儲存上擴充haspMap.enteySet,增加了before、after欄位。把hashMap的元素用雙向鏈表串連了起來。這個雙向鏈表決定了它的遍曆順序。
其順序一般是插入map中的順序,可是它有一個欄位accessOrder當為true時,遍曆順序將是LRU的效果。
研究它的有序性。我們能夠從put方法、get方法和遍曆的方法入手。首先看get方法:
/** * 直接調用父類的getEntry方法。關鍵在於 * e.recordAccess(this) 這句代碼 */ public V get(Object key) { Entry<K,V> e = (Entry<K,V>)getEntry(key); if (e == null) return null; e.recordAccess(this); return e.value; } /** * Entry.recordAccess 方法 * * 假設是訪問順序(accessOrder=true),那麼就把它放到頭結點的下一個位置 * 否則什麼也不做。 * 這樣就能夠依據初始 accessOrder 屬性,來決定遍曆的順序。 */ void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } }
Put方法:
/** * put方法調用此方法,覆蓋了父類中的實現, */ void addEntry(int hash,K key, V value, int bucketIndex) { createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate Entry<K,V> eldest = header.after; //回調。假設有必要移除在老的元素。最新的元素在鏈表尾部。 if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } else { if (size >= threshold) resize(2 * table.length); } } /** * */ void createEntry(int hash,K key, V value, int bucketIndex) { HashMap.Entry<K,V> old = table[bucketIndex]; Entry<K,V> e = new Entry<K,V>(hash, key, value, old); table[bucketIndex] = e; //本質是插入雙向鏈表的末尾 e.addBefore(header); size++; } /** * 插入到 existingEntry的前面,由於是雙向鏈表。當existingEntry是 header時。 * 相當於插入到鏈表最後。 * */ private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; }
遍曆迭代直接使用雙向鏈表進行迭代介面,這裡不贅述。能夠看原始碼非常easy理解。
注意的是實現上市覆蓋了父類中相關的產生迭代器的方法。
TreeMap和CurrentHashMap都能夠單獨開一篇文章來分析了。這裡簡單說下。TreeMap是基於b樹map,依據key排序。CurrentHashMap是並發包中的一個強大的類,適合多線程高並發時資料讀寫。
java之Map原始碼淺析