HashMap與HashTable~
這一塊主要整理一下HashMap與HashTable之間個別重要的點和細節以及這兩者之間的異同點!
首先簡單介紹一下何謂執行緒安全?
如果我們的程式碼所在的程序中有多個執行緒在同時執行,而且這些執行緒可能同時執行這段程式碼。如果每次的執行結果和單執行緒的執行結果是一樣的,而且其他的變數的值也和預期的是一樣的,那就是執行緒安全的。
一、HashMap
1、HashMap執行緒不安全體現在哪裡?
HashMap的預設容量是16,當在進行put的時候,插入的元素超過了容量(由載入因子決定)的範圍就會觸發擴容操作,也就是rehash過程,這個方法會重新將原陣列的內容重新hash到新的擴容陣列當中,在多執行緒的環境下,存在同時其他的元素也在進行put操作,如果hash值相同,可能出現在同一陣列下用連結串列表示,造成閉環,導致在獲取資料也就是進行get操作的時候,產生死迴圈,所以HashMap是執行緒不安全的。(本來應該畫圖解釋的,但是限於自身對這個多執行緒環境操作不太熟悉,也就直接給出文字總結版)。
2、HashMap的適用場景?
因為HashMap是非執行緒安全的,故其適用於單執行緒環境下。
底下的程式碼演示是上課沒寫出來,課下自己又練了練,貼在這裡。
//10萬個資料統計資料重複出現的次數並列印 //首先隨機產生10萬個原始資料 LinkedList list=new LinkedList(); Random random=new Random(); for(int i=0;i<100000;i++){ list.add(random.nextInt(100)); } //統計出現的次數 HashMap<Integer,Integer> map=new HashMap<>(); Iterator<Integer> iterator=list.iterator(); while(iterator.hasNext()){ Integer value=iterator.next(); Integer num=map.get(value); if(num==null){ map.put(value,1); }else{ map.put(value,num+1); } } //列印資料 System.out.println("統計10萬個資料重複出現的次數:"); Iterator<Map.Entry<Integer,Integer>> iterator1=map.entrySet().iterator(); while(iterator1.hasNext()){ Map.Entry<Integer,Integer> entry=iterator1.next(); Integer value1=entry.getKey(); Integer number=entry.getValue(); System.out.print(number+" "+value1 +" "); } System.out.println(); }
//統計10萬個資料,找出第一個重複的資料並進行列印 //方法一、 //首先隨機產生10萬個原始資料 LinkedList list=new LinkedList(); Random random=new Random(); for(int i=0;i<100000;i++){ list.add(random.nextInt(100)); } //統計第一個重複出現的資料 System.out.println("第一個重複的資料為:"); HashMap<Integer,Integer> map=new HashMap<>(); Iterator<Integer> iterator=list.iterator(); while(iterator.hasNext()){ Integer value=iterator.next();//資料本身,也就是鍵值key的位置 if(!map.containsKey(value)){//HashMap中如果不包含此鍵值,說明是第一次重複的資料 System.out.println(value+" "); return ; } } //方法二、 LinkedList list=new LinkedList(); Random random=new Random(); for(int i=0;i<100000;i++){ list.add(random.nextInt(100)); } System.out.println("第一次重複出現的資料為:"); HashMap<Integer,Integer> map=new HashMap<>(); Iterator<Integer> iterator=list.iterator(); while(iterator.hasNext()){ Integer value=iterator.next(); Integer num=map.get(value); if(num==null){ map.put(value,1); }else{ System.out.print(value+" "); return; } }
//統計10萬個資料,找出出現次數最多的資料並進行列印
LinkedList list=new LinkedList();
Random random=new Random();
for(int i=0;i<100000;i++){
list.add(random.nextInt(100));
}
Iterator<Integer> iterator=list.iterator();
HashMap<Integer,Integer> map=new HashMap<>();
Integer max=0;
Integer data=0;
while(iterator.hasNext()){
Integer value=iterator.next();
Integer num=map.get(value);
if(num==null){
map.put(value,1);
}else{
map.put(value,num+1);
}
}
System.out.println("統計10萬個資料中重複出現次數最多的資料為:");
Iterator<Map.Entry<Integer,Integer>> iterator1=map.entrySet().iterator();
while(iterator1.hasNext()){
Map.Entry<Integer,Integer> entry=iterator1.next();
Integer Data=entry.getKey();
Integer vvalue=entry.getValue();
if(vvalue>max){
max=vvalue;
data=Data;
}
}
System.out.println(data+"==>"+max);
二、HashTable
HashTable在JDK1.1.的時候就已經有了。
1、HashTable的執行緒安全體現在哪裡?
HashTable的put()、get()、remove()方法是關鍵字synchronized來修飾的,其保證了方法的同步性進而保證了其執行緒安全性。
synchronized關鍵字:
加上synchronized的關鍵字的方法,如果兩個執行緒同時操作該方法,表示這兩個過程是併發的。
不加synchronized關鍵字的方法,如果兩個執行緒同時操作該方法,表示這兩個過程是順次執行的,也就是一個過程執行完再執行另一個過程。
在多執行緒環境下,synchronized關鍵字非常常見,當需要進行“同步”操作的時候,需要該關鍵字對程式碼塊或者方法進行鎖定,被synchronized鎖定的程式碼塊,只能同時有一條執行緒訪問該程式碼塊。那麼,synchronized關鍵字是鎖物件還是鎖程式碼塊?答案是鎖物件!!!(在這裡,直接給出結論,後續可能會使用程式碼解釋該關鍵字修飾方法的詳細解析)
當synchronized修飾靜態方法時,表示鎖定的是class物件;修飾動態方法時,表示鎖定的是當前物件(this)。
當synchronized修飾程式碼塊時,表示鎖住的是某個物件,被該關鍵字修飾的其他執行緒的方法如果要執行,也必須得到該鎖,即synchronized程式碼塊執行完。
2、HashTable的適用場景?
HashTable是早期Java類庫提供的一個雜湊表表現,本身是同步的,不支援null值和空鍵,它的方法是同步方法,所帶來的問題的無論在什麼時刻,只能有一個執行緒來操作HashTable,效率比較低下,導致的效能開銷,現在已經很少使用了。
3、HashTable原始碼中使用的Entry與HashMap原始碼中的Entry一樣嗎?
答案是一樣的,原始碼分析如下:
//HashMap內部定義的靜態內部類Entry,實現Map.Entry<K,V>介面
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
根據上述的成員變數可以看出,該Entry是單向連結串列,只有一個next域指向下一個節點!!!
//HashTable定義的靜態內部類Entry,實現Map.Entry<K,V>介面
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;
final K key;
V value;
Entry<K,V> next;
根據定義的成員變數可以看出,是單向連結串列,只含有一個指向下一個節點的next域
4、HashTable使用的迭代器與之前集合實現類所使用的迭代器有何區別?
(Enumeration列舉遍歷與Iterator遍歷有何區別???)
HashTable使用的迭代器是Enumeration列舉遍歷元素的。
Enumeration列舉僅適用於傳統,例如HashTable、Vector,列舉是初始Java發行版JDK1.0的一部分,現在已經基本被Iteration迭代器所取代了。區別主要在於:
Enumeration Iteration
只有兩個函式介面,通過Enumeration,只能讀取 有三個函式介面,除了能讀取集合中的資料之外,也可以對集合
集合中的資料,但是不能對資料進行修改。 中的資料進行修改。
不支援fail-fast快速失敗機制 支援fail-fast快速失敗機制
三、HashMap與HashTable之間的異同點?
1、相同點
HashMap與HashTable都實現了Map介面,底層資料結構都是陣列加連結串列。
2、不同點:主要從繼承關係、初始預設容量的大小、執行緒安全性、擴容方式、null值的處理、hash函式、方法這八個方面來說明。
HashMap HashTable
繼承關係: extends AbstractMap extends Dictionary
初始預設容量的大小: 16 11
執行緒安全性: 非執行緒安全 執行緒安全的
擴容方式: resize(2 * table.length);(2的指數) 2倍擴容 newCapacity = oldCapacity * 2 + 1 2倍+1擴容
null值的處理: 最多隻允許一個key為null,value可多個為null key和value都不允許為null
hash函式: int h = hashSeed; hashSeed ^ k.hashCode();
if (0 != h && k instanceof String) { 首先判斷雜湊種子是否為0以及傳進的k是否為string
return sun.misc.Hashing.stringHash32((String) k); 如果滿足,呼叫特殊雜湊函式處理!
}
h ^= k.hashCode();
方法: 只含有ContainsKey(Object key)方法 含有ContainsKey(Object key)、
Contains(Object value)、 ContainsValue(Object value)