1. 程式人生 > >HashMap與HashTable~

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)