java之Map原始碼淺析

來源:互聯網
上載者:User

標籤: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原始碼淺析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.