Java深入集合--linkedHashMap

原文:http://greemranqq.iteye.com/blog/1931880

LinkedHashMap 源碼介紹

 

一、介紹:

     LinkedHashMap 和hashMap 功能類似,都是維護的鍵值對集合,連遍歷 以及方法都類似,唯一的區別在於

hashMap  裏面的元素是根據hash值來決定存放位置的,是無序的,而LinkedHashMap 維護的是一個按順序存放的雙向鏈表,是有序的。

      所謂的雙向鏈表其實是鏈表的一種。鏈表:相當於元素 A->B->C ,也就是我可以通過A 找到B ,從而找到C,可以單向移動。而雙向鏈表:A<->B<->C ,也就是說我可以從B  找到 A 和 C,可以雙向移動。

 

二、源碼分析:

       

Java代碼 複製代碼 收藏代碼
  1. public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>  
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

    我們可以看到 這東西是繼承的HashMap 的,說明擁有hashMap 的功能,那麼我們具體來看看 不同在哪裏呢?

 

   1. 構造函數:

    

Java代碼 複製代碼 收藏代碼
  1. public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {  
  2.         super(initialCapacity, loadFactor);  
  3.         this.accessOrder = accessOrder;  
  4. }  
  5.   
  6.  public LinkedHashMap(int initialCapacity, float loadFactor) {  
  7.         super(initialCapacity, loadFactor);  
  8.         accessOrder = false;  
  9. }  
  10.   
  11.  public LinkedHashMap(int initialCapacity) {  
  12.     super(initialCapacity);  
  13.         accessOrder = false;  
  14. }  
  15. 我們看到3個構造函數,其實都是訪問的super(..),也就是hashMap 的構造,區別就在accessOrder   
  16. 來看看它是什麼?  
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
}

 public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
}

 public LinkedHashMap(int initialCapacity) {
	super(initialCapacity);
        accessOrder = false;
}
我們看到3個構造函數,其實都是訪問的super(..),也就是hashMap 的構造,區別就在accessOrder 
來看看它是什麼?

 

 

Java代碼 複製代碼 收藏代碼
  1.  //The iteration ordering method for this linked hash map: <tt>true</tt>  
  2.  //for access-order, <tt>false</tt> for insertion-order.  
  3. // 簡單說就是這個用來控制元素的順序,  
  4. // true: 是訪問的順序,也就是誰最先訪問,就排在第一位  
  5. // false:存放順序,就是你put 元素的時候的順序  
  6. private final boolean accessOrder;  
 
     //The iteration ordering method for this linked hash map: <tt>true</tt>
     //for access-order, <tt>false</tt> for insertion-order.
    // 簡單說就是這個用來控制元素的順序,
    // true: 是訪問的順序,也就是誰最先訪問,就排在第一位
    // false:存放順序,就是你put 元素的時候的順序
    private final boolean accessOrder;

  這裏稍後再看怎麼用的,我們先來分析核心內部類 Entry 類,這幾乎是所有map 都需要的東西。

 

Java代碼 複製代碼 收藏代碼
  1. private static class Entry<K,V> extends HashMap.Entry<K,V> {  
  2.        // These fields comprise the doubly linked list used for iteration.  
  3.        // 這裏我們看到,Entry<K,V>  類裏面多了了兩個屬性,專門來方便我們進行鏈表的前後移動        // 的  
  4.        Entry<K,V> before, after;  
  5.        // 這裏調用了hashMap 的entry 構造,說明還是用的HashMap 的Entry  
  6. Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {  
  7.            super(hash, key, value, next);  
  8.        }  
  9.       // ..剩下的稍後說  
 private static class Entry<K,V> extends HashMap.Entry<K,V> {
        // These fields comprise the doubly linked list used for iteration.
        // 這裏我們看到,Entry<K,V>  類裏面多了了兩個屬性,專門來方便我們進行鏈表的前後移動        // 的
        Entry<K,V> before, after;
        // 這裏調用了hashMap 的entry 構造,說明還是用的HashMap 的Entry
	Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }
       // ..剩下的稍後說
}

   

 

 

Java代碼 複製代碼 收藏代碼
  1. 繼續來看這些屬性怎麼利用起來的。對於集合,我們肯定要關注他的 存放元素 和 取元素的方法啦:  
  2. 當我們找遍整個類的時候發現,沒有找到put 方法- -。但是LinkedHashMap 肯定是可以調用put的,因爲繼承了hashMap,  
  3. 那我們把 hashMap 的put方法先取出來吧  
繼續來看這些屬性怎麼利用起來的。對於集合,我們肯定要關注他的 存放元素 和 取元素的方法啦:
當我們找遍整個類的時候發現,沒有找到put 方法- -。但是LinkedHashMap 肯定是可以調用put的,因爲繼承了hashMap,
那我們把 hashMap 的put方法先取出來吧

 

Java代碼 複製代碼 收藏代碼
  1. public V put(K key, V value) {  
  2.        if (key == null)  
  3.            return putForNullKey(value);  
  4.        int hash = hash(key.hashCode());  
  5.        // 這裏都是調用 hashMap 計算位置什麼的,前面hashMap 已經分析過啦  
  6.        int i = indexFor(hash, table.length);  
  7.        for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
  8.            Object k;  
  9.            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  10.                V oldValue = e.value;  
  11.                e.value = value;  
  12.                // 這裏被重寫了 A  
  13.                e.recordAccess(this);  
  14.                return oldValue;  
  15.            }  
  16.        }  
  17.   
  18.        modCount++;  
  19.        // 這裏被重寫了 B   
  20.        addEntry(hash, key, value, i);  
  21.        return null;  
  22.    }  
 public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        // 這裏都是調用 hashMap 計算位置什麼的,前面hashMap 已經分析過啦
        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;
                // 這裏被重寫了 A
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        // 這裏被重寫了 B 
        addEntry(hash, key, value, i);
        return null;
    }

 

 

  從上面看出和hashMap 沒啥區別,關鍵就看重寫的方法啦,特別是e.recordAccess 這個方法 在hashMap     介紹中,不是提到不知道幹什麼的嘛,這裏詳細來看看。

    

Java代碼 複製代碼 收藏代碼
  1. private static class Entry<K,V> extends HashMap.Entry<K,V> {  
  2.     /** 
  3.          * This method is invoked by the superclass whenever the value 
  4.          * of a pre-existing entry is read by Map.get or modified by Map.set. 
  5.          * If the enclosing Map is access-ordered, it moves the entry 
  6.          * to the end of the list; otherwise, it does nothing. 
  7.          */  
  8.         void recordAccess(HashMap<K,V> m) {  
  9.             LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;  
  10.             // 如果參數爲true,也就是根據訪問順序  
  11.             if (lm.accessOrder) {  
  12.                 // 迭代控制的變量  
  13.                 lm.modCount++;  
  14.                 remove();  
  15.                 addBefore(lm.header);  
  16.             }  
  17.         }  
  18.       
  19.     // 這裏我們發現 before和after 雖然定義了屬性,是哪兒賦值的呢?爲什麼不爲null呢?  
  20.         // 請回到 構造函數,我們發現有一個init()方法,以前是沒用的,這裏也進行了重寫,請看         // 後面init 方法  
  21.         private void remove() {  
  22.             // 先移除當前這個空節點  
  23.             before.after = after;  
  24.             after.before = before;  
  25.         }  
  26.           
  27.     /** 
  28.          * Inserts this entry before the specified existing entry in the list. 
  29.          */  
  30.         private void addBefore(Entry<K,V> existingEntry) {  
  31.             // 然後將這個空節點 賦值  
  32.             after  = existingEntry;  
  33.             // 當前節點 ,賦值於標誌位的前節點  
  34.             before = existingEntry.before;  
  35.             // 然後將複製後的節點的 兩個屬性,繼續賦值爲空,等待另一個節點的插入  
  36.             before.after = this;  
  37.             after.before = this;  
  38.         }  
  39.         // 整個  
  40.   
  41. }  
private static class Entry<K,V> extends HashMap.Entry<K,V> {
	/**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         */
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            // 如果參數爲true,也就是根據訪問順序
            if (lm.accessOrder) {
                // 迭代控制的變量
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }
	
	// 這裏我們發現 before和after 雖然定義了屬性,是哪兒賦值的呢?爲什麼不爲null呢?
        // 請回到 構造函數,我們發現有一個init()方法,以前是沒用的,這裏也進行了重寫,請看         // 後面init 方法
        private void remove() {
            // 先移除當前這個空節點
            before.after = after;
            after.before = before;
        }
        
	/**
         * Inserts this entry before the specified existing entry in the list.
         */
        private void addBefore(Entry<K,V> existingEntry) {
            // 然後將這個空節點 賦值
            after  = existingEntry;
            // 當前節點 ,賦值於標誌位的前節點
            before = existingEntry.before;
            // 然後將複製後的節點的 兩個屬性,繼續賦值爲空,等待另一個節點的插入
            before.after = this;
            after.before = this;
        }
        // 整個

}

 

  init() 方法:

 

Java代碼 複製代碼 收藏代碼
  1.   /** 
  2.    * The head of the doubly linked list. 
  3.    */  
  4. private transient Entry<K,V> header;  
  5. void init() {  
  6.       // 這裏默鏈表 表頭是header,並讓hash值爲-1,其他都爲null,僅僅是作爲一個標誌位  
  7.       // 初始化這個節點  
  8.       header = new Entry<K,V>(-1nullnullnull);  
  9.       // 賦值在這裏,默認這是起點,相當於還沒有其他元素  
  10.       header.before = header.after = header;  
  11.   }  
    /**
     * The head of the doubly linked list.
     */
  private transient Entry<K,V> header;
  void init() {
        // 這裏默鏈表 表頭是header,並讓hash值爲-1,其他都爲null,僅僅是作爲一個標誌位
        // 初始化這個節點
        header = new Entry<K,V>(-1, null, null, null);
        // 賦值在這裏,默認這是起點,相當於還沒有其他元素
        header.before = header.after = header;
    }

 

   可能上面的文字描述很難理解,我們假設:這個雙鏈表結構相當於 一條一節一節連起來的項鍊,當然每一節肯定有鏈接前before 和 after 這樣的連接位置(屬性),中間纔是寶石(數據)。然後,一般項鍊都有一個明顯的位置,方便你去下來的(header),那裏其實是用來首位相連的,當我們覺得長度不夠,需要添加一個寶石的時候,首先會從那裏打開,也就是執行(remove)方法,然後把你需要的寶石連接在最後的位置(after  = existingEntry,before = existingEntry.before;);,從程序上來說 就是把那個header 的位置,原先連接器的先取掉掉,那個位置就從新連接新的元素。

    

繼續講解put 方法,繼續往下看 還有一個addEntry(hash, key, value, i)方法,也進行了重寫

 

Java代碼 複製代碼 收藏代碼
  1.    void addEntry(int hash, K key, V value, int bucketIndex) {  
  2.        // 存放元素  
  3.        createEntry(hash, key, value, bucketIndex);  
  4.   
  5.        // Remove eldest entry if instructed, else grow capacity if appropriate  
  6.        Entry<K,V> eldest = header.after;  
  7.        // 這裏始終返回的是false,也就是說 方便我們擴展,重寫的- -!  
  8.        if (removeEldestEntry(eldest)) {  
  9.            removeEntryForKey(eldest.key);  
  10.        } else {  
  11.            if (size >= threshold)  
  12.                resize(2 * table.length);  
  13.        }  
  14. }  
  15. void createEntry(int hash, K key, V value, int bucketIndex) {  
  16.        // 通過key 和長度 計算出的位置,去獲得那個元素,可能爲空  
  17.        HashMap.Entry<K,V> old = table[bucketIndex];  
  18.        // 然後在這個位置創建一個新元素,並讓next 指向old  
  19. Entry<K,V> e = new Entry<K,V>(hash, key, value, old);  
  20.        // 把這個元素放進數組的這個位置  
  21.        table[bucketIndex] = e;  
  22.        // 然後把header 的after before屬性,和元素節點從新連接起來  
  23.        // 元素就在header 之前了,也就是可以保證最先訪問(這裏通過Set 集合遍歷順序是反的- -!)  
  24.        e.addBefore(header);  
  25.        size++;  
  26.    }  
    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;
        // 這裏始終返回的是false,也就是說 方便我們擴展,重寫的- -!
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        } else {
            if (size >= threshold)
                resize(2 * table.length);
        }
 }
 void createEntry(int hash, K key, V value, int bucketIndex) {
        // 通過key 和長度 計算出的位置,去獲得那個元素,可能爲空
        HashMap.Entry<K,V> old = table[bucketIndex];
        // 然後在這個位置創建一個新元素,並讓next 指向old
	Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
        // 把這個元素放進數組的這個位置
        table[bucketIndex] = e;
        // 然後把header 的after before屬性,和元素節點從新連接起來
        // 元素就在header 之前了,也就是可以保證最先訪問(這裏通過Set 集合遍歷順序是反的- -!)
        e.addBefore(header);
        size++;
    }

 

   上面可以看出,linkedHashMap,元素默認是放在鏈表前,也就是根據存放順序放的,而實際情況還是用的hashMap 裏面的table 數組,元素位置是隨機存放的,只是linkedHashMap擴展,加入了屬性,對每個元素存放的位置進行了像鏈表結構那樣的鏈接。那麼當我們設置accessOrder=true 的時候如何才控制根據訪問順序進行排列呢?首先請看get 方法:

Java代碼 複製代碼 收藏代碼
  1. }    // 也進行了重寫  
  2. public V get(Object key) {  
  3.     // 調用父類的get方法  
  4.     Entry<K,V> e = (Entry<K,V>)getEntry(key);  
  5.     if (e == null)  
  6.         return null;  
  7.     // 這裏還是調用的剛纔的方法,把當前元素放在header 之前,也就完成了 根據訪問順序排序  
  8.     e.recordAccess(this);  
  9.     return e.value;  
  10. }  
    }    // 也進行了重寫
    public V get(Object key) {
        // 調用父類的get方法
        Entry<K,V> e = (Entry<K,V>)getEntry(key);
        if (e == null)
            return null;
        // 這裏還是調用的剛纔的方法,把當前元素放在header 之前,也就完成了 根據訪問順序排序
        e.recordAccess(this);
        return e.value;
    }

 

上面對該集合實現雙鏈表結構的原理 以及代碼大概講述了一下,可以去看看圖例更加清晰,下面繼續看看一些方法。

 

Java代碼 複製代碼 收藏代碼
  1. 1.keySet(),entrySet(),values方法:  
  2.  這裏還是調用的父類的,但是它重寫了幾個方法:  
  3.  // 這是重寫的  
  4.  Iterator<K> newKeyIterator()   { return new KeyIterator();   }  
  5.  Iterator<V> newValueIterator() { return new ValueIterator(); }  
  6.  terator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }  
  7.  // 這裏返回值方式類似,都是通過 nextEntry()返回不同的內容,但是繼承對象變成了LinkedHashIterator,不是原來的  // HashIterator  
  8.  private class KeyIterator extends LinkedHashIterator<K> {  
  9. public K next() { return nextEntry().getKey(); }  
  10.   }  
  11.   // 看看區別吧  
  12.   private abstract class LinkedHashIterator<T> implements Iterator<T> {  
  13.        // 後一個節點  
  14. Entry<K,V> nextEntry    = header.after;  
  15.        // 最後返回的節點  
  16. Entry<K,V> lastReturned = null;  
  17.   
  18. // 迭代的那個變量控制  
  19. int expectedModCount = modCount;  
  20.        // 如果下一個元素師頭元素,說明已經到底呢,沒有其他元素了  
  21.        public boolean hasNext() {  
  22.            return nextEntry != header;  
  23. }  
  24.       // 獲得下一個元素  
  25.       Entry<K,V> nextEntry() {  
  26.            // 這裏就不明白了,當排序參數設置爲true 是,有lm.modCount ++,這裏進行.next 迭代的時候,老報錯  
  27.            // 不明白的意義了  
  28.     if (modCount != expectedModCount)  
  29.     throw new ConcurrentModificationException();  
  30.            // 這裏元素迭代完了, 直接拋異常- -,不懂爲什麼這樣設計  
  31.            if (nextEntry == header)  
  32.                throw new NoSuchElementException();  
  33.            // 返回下一個元素,並且將nextEntry 指向下下一個元素  
  34.            Entry<K,V> e = lastReturned = nextEntry;  
  35.            nextEntry = e.after;  
  36.            return e;  
  37. }  
  38.        // 刪除  
  39.        public void remove() {  
  40.            // 這裏同上  
  41.     if (lastReturned == null)  
  42.     throw new IllegalStateException();  
  43.     if (modCount != expectedModCount)  
  44.     throw new ConcurrentModificationException();  
  45.     // 這裏看出lastReturned 作用就是記錄遍歷的當前位置,方便刪除  
  46.            // 這裏直接調用removeEntryForKey 方法就好了,這個- - 看着不爽,反正又不要返回值  
  47.            LinkedHashMap.this.remove(lastReturned.key);  
  48.            lastReturned = null;  
  49.            expectedModCount = modCount;  
  50. }  
  51.   }  
 1.keySet(),entrySet(),values方法:
  這裏還是調用的父類的,但是它重寫了幾個方法:
  // 這是重寫的
  Iterator<K> newKeyIterator()   { return new KeyIterator();   }
  Iterator<V> newValueIterator() { return new ValueIterator(); }
  terator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
  // 這裏返回值方式類似,都是通過 nextEntry()返回不同的內容,但是繼承對象變成了LinkedHashIterator,不是原來的  // HashIterator
  private class KeyIterator extends LinkedHashIterator<K> {
	public K next() { return nextEntry().getKey(); }
   }
   // 看看區別吧
   private abstract class LinkedHashIterator<T> implements Iterator<T> {
        // 後一個節點
	Entry<K,V> nextEntry    = header.after;
        // 最後返回的節點
	Entry<K,V> lastReturned = null;

	// 迭代的那個變量控制
	int expectedModCount = modCount;
        // 如果下一個元素師頭元素,說明已經到底呢,沒有其他元素了
        public boolean hasNext() {
            return nextEntry != header;
	}
       // 獲得下一個元素
       Entry<K,V> nextEntry() {
            // 這裏就不明白了,當排序參數設置爲true 是,有lm.modCount ++,這裏進行.next 迭代的時候,老報錯
            // 不明白的意義了
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
            // 這裏元素迭代完了, 直接拋異常- -,不懂爲什麼這樣設計
            if (nextEntry == header)
                throw new NoSuchElementException();
            // 返回下一個元素,並且將nextEntry 指向下下一個元素
            Entry<K,V> e = lastReturned = nextEntry;
            nextEntry = e.after;
            return e;
	}
        // 刪除
        public void remove() {
            // 這裏同上
	    if (lastReturned == null)
		throw new IllegalStateException();
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
	    // 這裏看出lastReturned 作用就是記錄遍歷的當前位置,方便刪除
            // 這裏直接調用removeEntryForKey 方法就好了,這個- - 看着不爽,反正又不要返回值
            LinkedHashMap.this.remove(lastReturned.key);
            lastReturned = null;
            expectedModCount = modCount;
	}
   }

 

 

Java代碼 複製代碼 收藏代碼
  1. 2.containsKey() 這裏是訪問hashMap 的方法,這裏就不說了。  
  2.    containsValue() 進行了重寫  
  3.   
  4.    public boolean containsValue(Object value) {  
  5.         // Overridden to take advantage of faster iterator  
  6.         // 這裏僅僅是通過鏈表的兩個屬性進行遍歷,hashMap 是通過table 數組進行遍歷,效果差不        // 多  
  7.         if (value==null) {  
  8.             for (Entry e = header.after; e != header; e = e.after)  
  9.                 if (e.value==null)  
  10.                     return true;  
  11.         } else {  
  12.             for (Entry e = header.after; e != header; e = e.after)  
  13.                 if (value.equals(e.value))  
  14.                     return true;  
  15.         }  
  16.         return false;  
2.containsKey() 這裏是訪問hashMap 的方法,這裏就不說了。
   containsValue() 進行了重寫

   public boolean containsValue(Object value) {
        // Overridden to take advantage of faster iterator
        // 這裏僅僅是通過鏈表的兩個屬性進行遍歷,hashMap 是通過table 數組進行遍歷,效果差不        // 多
        if (value==null) {
            for (Entry e = header.after; e != header; e = e.after)
                if (e.value==null)
                    return true;
        } else {
            for (Entry e = header.after; e != header; e = e.after)
                if (value.equals(e.value))
                    return true;
        }
        return false;

 

最近我不能直接在iteye 進行編輯,不知道爲什麼,老是卡死,都是記事本寫了,複製過來,沒圖片和格式,望見諒!

總結:1.linkedhashMap 是繼承於hashMap 也就是擁有了他一切功能

           2.他是雙鏈表結構,好處可以存取順序

           3.可以進行擴展,用來對那些最近訪問元素的優先獲得權

           4.存放效率,如果構造參數設置爲true ,由於要維護鏈表結構,效率比hashMap 低一點,但是默認是放在最後,能直接從header 進行操作,效率其實沒多大影響。

           5.get 元素類似。如果構造參數爲true ,需要進行鏈表的操作,效率低於hashMap,否則效率一樣。

           6.通過Iterator.keySet().iterator() 這樣迭代,數據3000000 的情況,數據默認一個數字,linkedhashMap 慢幾十毫秒,基本沒影響。如果構造參數爲true ,則linkedHashMap 迭代異常。

             

Java代碼 複製代碼 收藏代碼
  1.        // 我內存不夠,i大了要內存溢出  
  2.     public static  void test(){  
  3.     LinkedHashMap p = new  LinkedHashMap(3000000,0.75f,false);  
  4.     for(int i =0;i<3000000;i++){  
  5.         p.put(i, 2);  
  6.     }  
  7.     long a = System.currentTimeMillis();  
  8.     Iterator it1 = p.keySet().iterator();  
  9.     while(it1.hasNext()){  
  10.         Object o = it1.next();  
  11.         p.get(o);  
  12.     }  
  13.     long b = System.currentTimeMillis();  
  14.     System.out.println(b-a);// 250 毫秒  
  15.       
  16. }  
  17. public static void test2(){  
  18.     Map m = new HashMap(3000000);  
  19.     for(int i =0;i<3000000;i++){  
  20.         m.put(i, 2);  
  21.     }  
  22.     long a = System.currentTimeMillis();  
  23.     Iterator it1 = m.keySet().iterator();  
  24.     while(it1.hasNext()){  
  25.         Object o = it1.next();  
  26.         m.get(o);  
  27.     }  
  28.     long b = System.currentTimeMillis();  
  29.     System.out.println(b-a);// 210 毫秒  
  30. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章