Java中HashTable和HashMap

首先介紹一下HashTable和HashMap的區別:

1.HashMap是非線程安全的,HashTable是線程安全的;(線程安全就是線程同步的意思,就是當一個程序對一個線程安全的方法或者語句進行訪問的時候,其他的不能再對他進行操作瞭,必須等到這次訪問結束以後才能對這個線程安全的方法進行訪問)

2.HashMap的鍵或值都允許有null,而HashTable則不行。

3.因為線程安全的問題, HashMap要比HashTable效率高一點。

一、HashMap的內部存儲結構

下面介紹一下HashMap,轉載自其他帖子:

Java中數據存儲方式最底層的兩種結構,一種是數組,另一種就是鏈表,數組的特點:連續空間,尋址迅速,但是在刪除或者添加元素的時候需要有較大幅度的移動,所以查詢速度快,增刪較慢。而鏈表正好相反,由於空間不連續,尋址困難,增刪元素隻需修改指針,所以查詢慢、增刪快。有沒有一種數據結構來綜合一下數組和鏈表,以便發揮他們各自的優勢?答案是肯定的!就是:哈希表。哈希表具有較快(常量級)的查詢速度,及相對較快的增刪速度,所以很適合在海量數據的環境中使用。一般實現哈希表的方法采用“拉鏈法”,我們可以理解為“鏈表的數組”,如下圖:

從上圖中,我們可以發現哈希表是由數組+鏈表組成的,一個長度為16的數組中,每個元素存儲的是一個鏈表的頭結點。那麼這些元素是按照什麼樣的規則存儲到數組中呢。一般情況是通過hash(key)%len獲得,也就是元素的key的哈希值對數組長度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存儲在數組下標為12的位置。它的內部其實是用一個Entity數組來實現的,屬性有key、value、next。

1.初始化

默認就是開辟16個大小的空間。

2.put(key,value)操作

當調用put操作時,首先判斷key是否為null.

如果key是null,獲取Entry的第一個元素table[0],並基於第一個元素的next屬性開始遍歷,直到找到key為null的Entry,將其value設置為新的value值。如果沒有找到key為null的元素,則調用如上述代碼的addEntry(0, null, value, 0);增加一個新的entry

如果key不為null,首先會進行key.hashCode()操作,獲取key的哈希值,hashCode()是Object類的一個方法,為本地方法,內部實現比較復雜,函數中int i = indexFor(hash, table.length);的意思,相當於int i = hash % Entry[].length;得到i後,就是在Entry數組中的位置。

上面我們提到過Entry類裡面有一個next屬性,作用是指向下一個Entry。如, 第一個鍵值對A進來,通過計算其key的hash得到的i=0,記做:Entry[0] = A。一會後又進來一個鍵值對B,通過計算其i也等於0,現在怎麼辦?HashMap會這樣做:B.next = A,Entry[0] = B,如果又進來C,i也等於0,那麼C.next = B,Entry[0] = C;這樣我們發現i=0的地方其實存取瞭A,B,C三個鍵值對,他們通過next這個屬性鏈接在一起,也就是說數組中存儲的是最後插入的元素。

3.get(Object key)操作

get(Object key)操作時根據鍵來獲取值,如果瞭解瞭put操作,get操作容易理解

1、當key為null時,調用getForNullKey()。

2、當key不為null時,先根據hash函數得到hash值,在更具indexFor()得到i的值,循環遍歷鏈表,如果有:key值等於已存在的key值,則返回其value。

二、HashTable的內部儲存結構

HashTable和HashMap采用相同的存儲機制,二者的實現基本一致,不同的是:

1、HashMap是非線程安全的,HashTable是線程安全的,內部的方法基本都是synchronized。

2、HashTable不允許有null值的存在。

在HashTable中調用put方法時,如果key為null,直接拋出NullPointerException。其它細微的差別還有,比如初始化Entry數組的大小等等,但基本思想和HashMap一樣。

三、HashTable和ConcurrentHashMap的比較

如果面試的時候能夠答出HashMap和HashTable的上述3個區別,簡單的面試算是過瞭。如果在問Java中的另一個線程安全的與HashMap及其類似的類是什麼?同樣是線程安全,它與HashTable在線程同步上有什麼不同?能把第二個問題完整的答出來,說明你的基礎算是不錯的瞭。

這個類就是我們要講的ConcurrentHashMap類:

ConcurrentHashMap是線程安全的HashMap的實現。是線程安全的類。

synchronized關鍵字加鎖的原理,其實是對對象加鎖,不論你是在方法前加synchronized還是語句塊前加,鎖住的都是對象整體,但是ConcurrentHashMap的同步機制和這個不同,它不是加synchronized關鍵字,而是基於lock操作的,這樣的目的是保證同步的時候,鎖住的不是整個對象。

1、構造方法

為瞭容易理解,我們先從構造函數說起。ConcurrentHashMap是基於一個叫Segment數組的,其實和Entry類似

2、put操作

與HashMap不同的是,如果key為null,直接拋出NullPointer異常,之後,同樣先計算hashCode的值,再計算hash值,不過此處hash函數和HashMap中的不一樣:

若需要源代碼加以理解:http://blog.csdn.net/tgxblue/article/details/8479147

赞(0)