1. 程式人生 > >JAVA基礎第五章-集合框架Map篇 JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性:封裝、繼承、多型 JAVA基礎第三章-類與物件、抽象類、介面 JAVA基礎第四章-集合框架Collection篇

JAVA基礎第五章-集合框架Map篇 JAVA基礎第一章-初識java JAVA基礎第二章-java三大特性:封裝、繼承、多型 JAVA基礎第三章-類與物件、抽象類、介面 JAVA基礎第四章-集合框架Collection篇

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地!

我將會持續更新java基礎知識,歡迎關注。

 

往期章節:

JAVA基礎第一章-初識java

JAVA基礎第二章-java三大特性:封裝、繼承、多型

JAVA基礎第三章-類與物件、抽象類、介面   

JAVA基礎第四章-集合框架Collection篇

 


 

   在上一章節中,我們講了集合框架的Collection部分,下面我們來講一下Map介面

  我們再看一下集合框架的結構圖

map介面的實現類大致有 HashMap、LinkedHahMap、TreeMap、HashTable 四類。

 

 

Map

從這個名字上我們可以知道是“地圖、對映”的意思。

map集合使用鍵(key)值(value)來儲存資料,其中值(value)可以重複,但鍵(key)必須是唯一,也可以為空,但最多隻能有一個key為空。

HashMap

 對於hashMap,我們需要從名字中的hash入手,去分析他,hash,我們經常叫做雜湊表又或者叫做雜湊函式。

在 JDK 中,Object 的 hashcode 方法是本地方法,也就是用 c 語言或 c++ 實現的,該方法直接返回物件的 記憶體地址。

先看一張圖

 如上圖中的結構過程整個形成過程大致如下:

1,假如我們先插入一個鍵值對,然後進行hash後,存放在了陣列的1號位置;

2,然後我們再插入一個鍵值對 ,經過hash後,存在了4號位置,;

3,再然後我們又插入了一個鍵值對,這個時候很不幸,與1號位置衝突,然後這個時候會在1號位置之後追加一個連結串列,讓1號位置的Entry中的next指向這次最新追加的這一個鍵值對,以此類推;

從上圖中我們大概可以瞭解到,整個的HashMap的資料結構就是 陣列+連結串列(在jdk1.8中確切的說是Node物件,只不過Node實現了Entry介面),我們先看看Node 類的程式碼:

從上面的程式碼我們可以看出主要有4個屬性,key  value、 hash、以及再包含一個自身的類物件Node~ 這部分其實和我們上一節講過的LinkedList的資料結構很像

當我們在程式碼中我們會呼叫put方法,原始碼如下:

 1     /**
 2      * Associates the specified value with the specified key in this map.
 3      * If the map previously contained a mapping for the key, the old
 4      * value is replaced.
 5      *
 6      * @param key key with which the specified value is to be associated
 7      * @param value value to be associated with the specified key
 8      * @return the previous value associated with <tt>key</tt>, or
 9      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
10      *         (A <tt>null</tt> return can also indicate that the map
11      *         previously associated <tt>null</tt> with <tt>key</tt>.)
12      */
13     public V put(K key, V value) {
14         return putVal(hash(key), key, value, false, true);
15     }

從註釋中我們可以瞭解大意:“關聯一個指定的值和鍵在這個map 如果map顯然已經包含了這個鍵的對映,那麼舊值就會被替代”

在這個程式碼中繼續呼叫putval方法,我們繼續看原始碼:

 1   /**
 2      * Implements Map.put and related methods
 3      *
 4      * @param hash hash for key
 5      * @param key the key
 6      * @param value the value to put
 7      * @param onlyIfAbsent if true, don't change existing value
 8      * @param evict if false, the table is in creation mode.
 9      * @return previous value, or null if none
10      */
11     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
12                    boolean evict) {
13         Node<K,V>[] tab; Node<K,V> p; int n, i;
14         if ((tab = table) == null || (n = tab.length) == 0)
15             n = (tab = resize()).length;
16         if ((p = tab[i = (n - 1) & hash]) == null)
17             tab[i] = newNode(hash, key, value, null);
18         else {
19             Node<K,V> e; K k;
20             if (p.hash == hash &&
21                 ((k = p.key) == key || (key != null && key.equals(k))))
22                 e = p;
23             else if (p instanceof TreeNode)
24                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
25             else {
26                 for (int binCount = 0; ; ++binCount) {
27                     if ((e = p.next) == null) {
28                         p.next = newNode(hash, key, value, null);
29                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
30                             treeifyBin(tab, hash);
31                         break;
32                     }
33                     if (e.hash == hash &&
34                         ((k = e.key) == key || (key != null && key.equals(k))))
35                         break;
36                     p = e;
37                 }
38             }
39             if (e != null) { // existing mapping for key
40                 V oldValue = e.value;
41                 if (!onlyIfAbsent || oldValue == null)
42                     e.value = value;
43                 afterNodeAccess(e);
44                 return oldValue;
45             }
46         }
47         ++modCount;
48         if (++size > threshold)
49             resize();
50         afterNodeInsertion(evict);
51         return null;
52     }

從註釋中我們可以知道:實現map.put 和相關的方法

從第14行程式碼開始意思是,這個map如果是空的,那先初始化一個數組,然後再new一個 Node物件,放入陣列中,具體的位置根據陣列的長度和hash的  “與”  值確定 ;

如果已經有元素存在map中,則程式碼從19行開始執行,對於這塊大致意思就是,如果已經存在相同的hashcode ,那麼會判斷鍵是否相同,如果也相同則替換原有的值。

另外關於hashMap的擴容,我們需要了解的是預設的容量是16 過載因子是0.75 什麼意思呢?

就是說當集合中的資料量大小達到了門限值 16*0.75=12,那再新增元素進去,就會對當前的集合進行擴容,擴容為原來的2倍。

注意,這裡是根據門限值進行擴容也就是12,而不是總容量16。

 

TreeMap

根據名字我們可以知道這是一個和樹有關的map,關於treemap的結構圖大致如下:

 

從上圖中可以大概瞭解到,treemap的實現底層是紅黑樹,瞭解這個的同學相信也就能知道為什麼treemap是有序的了。

這個圖中的每一個節點物件都是Entry,他和hashmap中的不一樣,而是一個類,不是介面,程式碼如下圖所示:

從上圖圈中可以瞭解到,每一個節點都包含 key、value、 以及Entry型別的 left、right、parent 屬性

上述結構圖形成過程大致描述如下:

1,我們先插入一個鍵值對,鍵為2;

2,再插入一個鍵為1,這個時候,會將這個鍵值對放在上一個的左子節點部分;

3,再插入一個3,這個時候,會將他放在第一個節點的右子節點部分;

4,如果再插入一個4,那這個時候,會把前面3個整體作為他的左子節點,以此類推;

我們再來看看原始碼:

 1   /**
 2      * Associates the specified value with the specified key in this map.
 3      * If the map previously contained a mapping for the key, the old
 4      * value is replaced.
 5      *
 6      * @param key key with which the specified value is to be associated
 7      * @param value value to be associated with the specified key
 8      *
 9      * @return the previous value associated with {@code key}, or
10      *         {@code null} if there was no mapping for {@code key}.
11      *         (A {@code null} return can also indicate that the map
12      *         previously associated {@code null} with {@code key}.)
13      * @throws ClassCastException if the specified key cannot be compared
14      *         with the keys currently in the map
15      * @throws NullPointerException if the specified key is null
16      *         and this map uses natural ordering, or its comparator
17      *         does not permit null keys
18      */
19     public V put(K key, V value) {
20         Entry<K,V> t = root;
21         if (t == null) {
22             compare(key, key); // type (and possibly null) check
23 
24             root = new Entry<>(key, value, null);
25             size = 1;
26             modCount++;
27             return null;
28         }
29         int cmp;
30         Entry<K,V> parent;
31         // split comparator and comparable paths
32         Comparator<? super K> cpr = comparator;
33         if (cpr != null) {
34             do {
35                 parent = t;
36                 cmp = cpr.compare(key, t.key);
37                 if (cmp < 0)
38                     t = t.left;
39                 else if (cmp > 0)
40                     t = t.right;
41                 else
42                     return t.setValue(value);
43             } while (t != null);
44         }
45         else {
46             if (key == null)
47                 throw new NullPointerException();
48             @SuppressWarnings("unchecked")
49                 Comparable<? super K> k = (Comparable<? super K>) key;
50             do {
51                 parent = t;
52                 cmp = k.compareTo(t.key);
53                 if (cmp < 0)
54                     t = t.left;
55                 else if (cmp > 0)
56                     t = t.right;
57                 else
58                     return t.setValue(value);
59             } while (t != null);
60         }
61         Entry<K,V> e = new Entry<>(key, value, parent);
62         if (cmp < 0)
63             parent.left = e;
64         else
65             parent.right = e;
66         fixAfterInsertion(e);
67         size++;
68         modCount++;
69         return null;
70     }

從註釋可以瞭解,和hashmap表達的意思一樣,再看程式碼中,如果還沒有初始化,那先進行初始化root節點,如果有指定比較器,那就根據指定的比較器,進行比較。

然後後面的程式碼就是根據插入的鍵進行大小的比較,然後設定各種左右子父節點屬性等。

 

LinkedHahMap

 關於linkedHashMap,從名字可以知道他是和連結串列有關係的,結構圖如下所示:

注意圖中的灰色箭頭,在這裡我們需要和一開始我們講的hashmap中的結構圖,做一個比較,在hashmap中雖然也有連結串列,但是我們要知道,那個連結串列只是為了解決hash衝突,連結串列中的元素互相之間沒有什麼關係,不代表誰早誰晚,或者說誰大誰小。

而這裡的連結串列中比hashmap多了一個before 和after,如下圖:

 

那麼這裡的before 和after就確定了插入的順序先後。

 

 HashTable

 關於hashTable基本上和HashMap結構一致,不再贅述,唯一區別就是他是執行緒安全的,而且不允許null value,如果有直接丟擲異常

為什麼是執行緒安全的?我們看如下原始碼:

 1   /**
 2      * Maps the specified <code>key</code> to the specified
 3      * <code>value</code> in this hashtable. Neither the key nor the
 4      * value can be <code>null</code>. <p>
 5      *
 6      * The value can be retrieved by calling the <code>get</code> method
 7      * with a key that is equal to the original key.
 8      *
 9      * @param      key     the hashtable key
10      * @param      value   the value
11      * @return     the previous value of the specified key in this hashtable,
12      *             or <code>null</code> if it did not have one
13      * @exception  NullPointerException  if the key or value is
14      *               <code>null</code>
15      * @see     Object#equals(Object)
16      * @see     #get(Object)
17      */
18     public synchronized V put(K key, V value) {
19         // Make sure the value is not null
20         if (value == null) {
21             throw new NullPointerException();
22         }
23 
24         // Makes sure the key is not already in the hashtable.
25         Entry<?,?> tab[] = table;
26         int hash = key.hashCode();
27         int index = (hash & 0x7FFFFFFF) % tab.length;
28         @SuppressWarnings("unchecked")
29         Entry<K,V> entry = (Entry<K,V>)tab[index];
30         for(; entry != null ; entry = entry.next) {
31             if ((entry.hash == hash) && entry.key.equals(key)) {
32                 V old = entry.value;
33                 entry.value = value;
34                 return old;
35             }
36         }
37 
38         addEntry(hash, key, value, index);
39         return null;
40     }

從上面的方法中加上的 synchronized 關鍵詞,我們就可以知道了。

另外要注意的是hashTable已經不建議使用了,取而代之的是ConcurrentHashMap ,因為他既保證了執行緒的安全性,又進一步提高了,效率。

四個實現類的異同:

1. HashMap是基於雜湊表(hash table)實現,其keys和values都沒有順序。
2. TreeMap是基於紅黑樹(red-black tree)實現,按照keys排序元素,可以指定排序規則,如果不指定則按照自然排序。
3. LinkedHashMap是基於雜湊表(hash table)實現,按照插入順序排序元素。
4. Hashtable區別與HashMap的地方只有,它是同步的(synchronized),因此效能較低些。為了效能,線上程安全的程式碼中,優先考慮使用HashMap。

 

Map的遍歷

對於map的遍歷大致有以下3種方式:

 1 //第1種只遍歷鍵或者值,通過加強for迴圈
 2         for(String s1:map.keySet()){//遍歷map的鍵
 3             System.out.println("鍵key :"+s1);
 4         }
 5         for(String s2:map.values()){//遍歷map的值
 6             System.out.println("值value :"+s2);
 7         }
 8         9         
10         //第2種方式Map.Entry<String, String>的加強for迴圈遍歷輸出鍵key和值value
11         for(Map.Entry<String, String> entry : map.entrySet()){
12             System.out.println("鍵 key :"+entry.getKey()+" 值value :"+entry.getValue());
13         }
14        15         
16         //第3種Iterator遍歷獲取,然後獲取到Map.Entry<String, String>,再得到getKey()和getValue()
17         Iterator<Map.Entry<String, String>> it=map.entrySet().iterator();
18         while(it.hasNext()){
19             Map.Entry<String, String> entry=it.next();
20             System.out.println("鍵key :"+entry.getKey()+" value :"+entry.getValue());
21         }

 

比較器

在這裡說以下Comparable 、 Comparator 2個介面

Comparable

只有一個方法compareto,而且是通過返回的int型數值判斷大小

1、比較者大於被比較者(也就是compareTo方法裡面的物件),那麼返回正整數

2、比較者等於被比較者,那麼返回0

3、比較者小於被比較者,那麼返回負整數

Comparator

Comparator接口裡面有一個compare方法,方法有兩個引數T o1和T o2,是泛型的表示方式,分別表示待比較的兩個物件,方法返回值和Comparable介面一樣是int,有三種情況:

1、o1大於o2,返回正整數

2、o1等於o2,返回0

3、o1小於o3,返回負整數

二者之間的區別:

1,比較方法引數數不同;

2,後者介面中提供了更多的方法可以供使用;

3,前者只能和自己比較,後者可以對其他2個類進行比較;

 

 

關於集合部分,到這裡我們就全部結束了。

 注;以上原始碼分析都是基於jdk1.8

 

 


 

文中若有不正之處,歡迎批評指正!

 業內經常說的一句話是不要重複造輪子,但是有時候,只有自己造一個輪子了,才會深刻明白什麼樣的輪子適合山路,什麼樣的輪子適合平地!

我將會持續更新java基礎知識,歡迎關注。

 

往期章節:

JAVA基礎第一章-初識java

JAVA基礎第二章-java三大特性:封裝、繼承、多型

JAVA基礎第三章-類與物件、抽象類、介面   

JAVA基礎第四章-集合框架Collection篇

 


 

   在上一章節中,我們講了集合框架的Collection部分,下面我們來講一下Map介面

  我們再看一下集合框架的結構圖

map介面的實現類大致有 HashMap、LinkedHahMap、TreeMap、HashTable 四類。

 

 

Map

從這個名字上我們可以知道是“地圖、對映”的意思。

map集合使用鍵(key)值(value)來儲存資料,其中值(value)可以重複,但鍵(key)必須是唯一,也可以為空,但最多隻能有一個key為空。

HashMap

 對於hashMap,我們需要從名字中的hash入手,去分析他,hash,我們經常叫做雜湊表又或者叫做雜湊函式。

在 JDK 中,Object 的 hashcode 方法是本地方法,也就是用 c 語言或 c++ 實現的,該方法直接返回物件的 記憶體地址。

先看一張圖

 如上圖中的結構過程整個形成過程大致如下:

1,假如我們先插入一個鍵值對,然後進行hash後,存放在了陣列的1號位置;

2,然後我們再插入一個鍵值對 ,經過hash後,存在了4號位置,;

3,再然後我們又插入了一個鍵值對,這個時候很不幸,與1號位置衝突,然後這個時候會在1號位置之後追加一個連結串列,讓1號位置的Entry中的next指向這次最新追加的這一個鍵值對,以此類推;

從上圖中我們大概可以瞭解到,整個的HashMap的資料結構就是 陣列+連結串列(在jdk1.8中確切的說是Node物件,只不過Node實現了Entry介面),我們先看看Node 類的程式碼:

從上面的程式碼我們可以看出主要有4個屬性,key  value、 hash、以及再包含一個自身的類物件Node~ 這部分其實和我們上一節講過的LinkedList的資料結構很像

當我們在程式碼中我們會呼叫put方法,原始碼如下:

 1     /**
 2      * Associates the specified value with the specified key in this map.
 3      * If the map previously contained a mapping for the key, the old
 4      * value is replaced.
 5      *
 6      * @param key key with which the specified value is to be associated
 7      * @param value value to be associated with the specified key
 8      * @return the previous value associated with <tt>key</tt>, or
 9      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
10      *         (A <tt>null</tt> return can also indicate that the map
11      *         previously associated <tt>null</tt> with <tt>key</tt>.)
12      */
13     public V put(K key, V value) {
14         return putVal(hash(key), key, value, false, true);
15     }

從註釋中我們可以瞭解大意:“關聯一個指定的值和鍵在這個map 如果map顯然已經包含了這個鍵的對映,那麼舊值就會被替代”

在這個程式碼中繼續呼叫putval方法,我們繼續看原始碼:

 1   /**
 2      * Implements Map.put and related methods
 3      *
 4      * @param hash hash for key
 5      * @param key the key
 6      * @param value the value to put
 7      * @param onlyIfAbsent if true, don't change existing value
 8      * @param evict if false, the table is in creation mode.
 9      * @return previous value, or null if none
10      */
11     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
12                    boolean evict) {
13         Node<K,V>[] tab; Node<K,V> p; int n, i;
14         if ((tab = table) == null || (n = tab.length) == 0)
15             n = (tab = resize()).length;
16         if ((p = tab[i = (n - 1) & hash]) == null)
17             tab[i] = newNode(hash, key, value, null);
18         else {
19             Node<K,V> e; K k;
20             if (p.hash == hash &&
21                 ((k = p.key) == key || (key != null && key.equals(k))))
22                 e = p;
23             else if (p instanceof TreeNode)
24                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
25             else {
26                 for (int binCount = 0; ; ++binCount) {
27                     if ((e = p.next) == null) {
28                         p.next = newNode(hash, key, value, null);
29                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
30                             treeifyBin(tab, hash);
31                         break;
32                     }
33                     if (e.hash == hash &&
34                         ((k = e.key) == key || (key != null && key.equals(k))))
35                         break;
36                     p = e;
37                 }
38             }
39             if (e != null) { // existing mapping for key
40                 V oldValue = e.value;
41                 if (!onlyIfAbsent || oldValue == null)
42                     e.value = value;
43                 afterNodeAccess(e);
44                 return oldValue;
45             }
46         }
47         ++modCount;
48         if (++size > threshold)
49             resize();
50         afterNodeInsertion(evict);
51         return null;
52     }

從註釋中我們可以知道:實現map.put 和相關的方法

從第14行程式碼開始意思是,這個map如果是空的,那先初始化一個數組,然後再new一個 Node物件,放入陣列中,具體的位置根據陣列的長度和hash的  “與”  值確定 ;

如果已經有元素存在map中,則程式碼從19行開始執行,對於這塊大致意思就是,如果已經存在相同的hashcode ,那麼會判斷鍵是否相同,如果也相同則替換原有的值。

另外關於hashMap的擴容,我們需要了解的是預設的容量是16 過載因子是0.75 什麼意思呢?

就是說當集合中的資料量大小達到了門限值 16*0.75=12,那再新增元素進去,就會對當前的集合進行擴容,擴容為原來的2倍。

注意,這裡是根據門限值進行擴容也就是12,而不是總容量16。

 

TreeMap

根據名字我們可以知道這是一個和樹有關的map,關於treemap的結構圖大致如下:

 

從上圖中可以大概瞭解到,treemap的實現底層是紅黑樹,瞭解這個的同學相信也就能知道為什麼treemap是有序的了。

這個圖中的每一個節點物件都是Entry,他和hashmap中的不一樣,而是一個類,不是介面,程式碼如下圖所示:

從上圖圈中可以瞭解到,每一個節點都包含 key、value、 以及Entry型別的 left、right、parent 屬性

上述結構圖形成過程大致描述如下:

1,我們先插入一個鍵值對,鍵為2;

2,再插入一個鍵為1,這個時候,會將這個鍵值對放在上一個的左子節點部分;

3,再插入一個3,這個時候,會將他放在第一個節點的右子節點部分;

4,如果再插入一個4,那這個時候,會把前面3個整體作為他的左子節點,以此類推;

我們再來看看原始碼:

 1   /**
 2      * Associates the specified value with the specified key in this map.
 3      * If the map previously contained a mapping for the key, the old
 4      * value is replaced.
 5      *
 6      * @param key key with which the specified value is to be associated
 7      * @param value value to be associated with the specified key
 8      *
 9      * @return the previous value associated with {@code key}, or
10      *         {@code null} if there was no mapping for {@code key}.
11      *         (A {@code null} return can also indicate that the map
12      *         previously associated {@code null} with {@code key}.)
13      * @throws ClassCastException if the specified key cannot be compared
14      *         with the keys currently in the map
15      * @throws NullPointerException if the specified key is null
16      *         and this map uses natural ordering, or its comparator
17      *         does not permit null keys
18      */
19     public V put(K key, V value) {
20         Entry<K,V> t = root;
21         if (t == null) {
22             compare(key, key); // type (and possibly null) check
23 
24             root = new Entry<>(key, value, null);
25             size = 1;
26             modCount++;
27             return null;
28         }
29         int cmp;
30         Entry<K,V> parent;
31         // split comparator and comparable paths
32         Comparator<? super K> cpr = comparator;
33         if (cpr != null) {
34             do {
35                 parent = t;
36                 cmp = cpr.compare(key, t.key);
37                 if (cmp < 0)
38                     t = t.left;
39                 else if (cmp > 0)
40                     t = t.right;
41                 else
42                     return t.setValue(value);
43             } while (t != null);
44         }
45         else {
46             if (key == null)
47                 throw new NullPointerException();
48             @SuppressWarnings("unchecked")
49                 Comparable<? super K> k = (Comparable<? super K>) key;
50             do {
51                 parent = t;
52                 cmp = k.compareTo(t.key);
53                 if (cmp < 0)
54                     t = t.left;
55                 else if (cmp > 0)
56                     t = t.right;
57                 else
58                     return t.setValue(value);
59             } while (t != null);
60         }
61         Entry<K,V> e = new Entry<>(key, value, parent);
62         if (cmp < 0)
63             parent.left = e;
64         else
65             parent.right = e;
66         fixAfterInsertion(e);
67         size++;
68         modCount++;
69         return null;
70     }

從註釋可以瞭解,和hashmap表達的意思一樣,再看程式碼中,如果還沒有初始化,那先進行初始化root節點,如果有指定比較器,那就根據指定的比較器,進行比較。

然後後面的程式碼就是根據插入的鍵進行大小的比較,然後設定各種左右子父節點屬性等。

 

LinkedHahMap

 關於linkedHashMap,從名字可以知道他是和連結串列有關係的,結構圖如下所示:

注意圖中的灰色箭頭,在這裡我們需要和一開始我們講的hashmap中的結構圖,做一個比較,在hashmap中雖然也有連結串列,但是我們要知道,那個連結串列只是為了解決hash衝突,連結串列中的元素互相之間沒有什麼關係,不代表誰早誰晚,或者說誰大誰小。

而這裡的連結串列中比hashmap多了一個before 和after,如下圖:

 

那麼這裡的before 和after就確定了插入的順序先後。

 

 HashTable

 關於hashTable基本上和HashMap結構一致,不再贅述,唯一區別就是他是執行緒安全的,而且不允許null value,如果有直接丟擲異常

為什麼是執行緒安全的?我們看如下原始碼:

 1   /**
 2      * Maps the specified <code>key</code> to the specified
 3      * <code>value</code> in this hashtable. Neither the key nor the
 4      * value can be <code>null</code>. <p>
 5      *
 6      * The value can be retrieved by calling the <code>get</code> method
 7      * with a key that is equal to the original key.
 8      *
 9      * @param      key     the hashtable key
10      * @param      value   the value
11      * @return     the previous value of the specified key in this hashtable,
12      *             or <code>null</code> if it did not have one
13      * @exception  NullPointerException  if the key or value is
14      *               <code>null</code>
15      * @see     Object#equals(Object)
16      * @see     #get(Object)
17      */
18     public synchronized V put(K key, V value) {
19         // Make sure the value is not null
20         if (value == null) {
21             throw new NullPointerException();
22         }
23 
24         // Makes sure the key is not already in the hashtable.
25         Entry<?,?> tab[] = table;
26         int hash = key.hashCode();
27         int index = (hash & 0x7FFFFFFF) % tab.length;
28         @SuppressWarnings("unchecked")
29         Entry<K,V> entry = (Entry<K,V>)tab[index];
30         for(; entry != null ; entry = entry.next) {
31             if ((entry.hash == hash) && entry.key.equals(key)) {
32                 V old = entry.value;
33                 entry.value = value;
34                 return old;
35             }
36         }
37 
38         addEntry(hash, key, value, index);
39         return null;
40     }

從上面的方法中加上的 synchronized 關鍵詞,我們就可以知道了。

另外要注意的是hashTable已經不建議使用了,取而代之的是ConcurrentHashMap ,因為他既保證了執行緒的安全性,又進一步提高了,效率。

四個實現類的異同:

1. HashMap是基於雜湊表(hash table)實現,其keys和values都沒有順序。
2. TreeMap是基於紅黑樹(red-black tree)實現,按照keys排序元素,可以指定排序規則,如果不指定則按照自然排序。
3. LinkedHashMap是基於雜湊表(hash table)實現,按照插入順序排序元素。
4. Hashtable區別與HashMap的地方只有,它是同步的(synchronized),因此效能較低些。為了效能,線上程安全的程式碼中,優先考慮使用HashMap。

 

Map的遍歷

對於map的遍歷大致有以下3種方式:

 1 //第1種只遍歷鍵或者值,通過加強for迴圈
 2         for(String s1:map.keySet()){//遍歷map的鍵
 3             System.out.println("鍵key :"+s1);
 4         }
 5         for(String s2:map.values()){//遍歷map的值
 6             System.out.println("值value :"+s2);
 7         }
 8         9         
10         //第2種方式Map.Entry<String, String>的加強for迴圈遍歷輸出鍵key和值value
11         for(Map.Entry<String, String> entry : map.entrySet()){
12             System.out.println("鍵 key :"+entry.getKey()+" 值value :"+entry.getValue());
13         }
14        15         
16         //第3種Iterator遍歷獲取,然後獲取到Map.Entry<String, String>,再得到getKey()和getValue()
17         Iterator<Map.Entry<String, String>> it=map.entrySet().iterator();
18         while(it.hasNext()){
19             Map.Entry<String, String> entry=it.next();
20             System.out.println("鍵key :"+entry.getKey()+" value :"+entry.getValue());
21         }

 

比較器

在這裡說以下Comparable 、 Comparator 2個介面

Comparable

只有一個方法compareto,而且是通過返回的int型數值判斷大小

1、比較者大於被比較者(也就是compareTo方法裡面的物件),那麼返回正整數

2、比較者等於被比較者,那麼返回0

3、比較者小於被比較者,那麼返回負整數

Comparator

Comparator接口裡面有一個compare方法,方法有兩個引數T o1和T o2,是泛型的表示方式,分別表示待比較的兩個物件,方法返回值和Comparable介面一樣是int,有三種情況:

1、o1大於o2,返回正整數

2、o1等於o2,返回0

3、o1小於o3,返回負整數

二者之間的區別:

1,比較方法引數數不同;

2,後者介面中提供了更多的方法可以供使用;

3,前者只能和自己比較,後者可以對其他2個類進行比較;

 

 

關於集合部分,到這裡我們就全部結束了。

 注;以上原始碼分析都是基於jdk1.8

 

 


 

文中若有不正之處,歡迎批評指正!