java集合HashMap、HashTable、HashSet詳解

1、Set和Map關係數組

Set表明集合元素無序,集合元素不可重複的集合,Map表明一種由多個key-value組成的集合,map集合是set集合的擴展只是名稱不一樣,對應以下安全

2、HashMap的工做原理框架

        HashMap基於 hashing原理,經過put()和get()方法儲存和獲取對象。
        put()方法: 它調用鍵對象的hashCode()方法來計算hashcode值,系統根據hashcode值決定該元素在bucket位置。若是兩個對象key的hashcode返回值相同,那他們的存儲位置相同,若是這兩個Entry的key經過equals比較返回true,新添加Entry的value將覆蓋集合中原有Entry的value,但key不會覆蓋;若是這兩個Entry的key經過equals比較返回false,新添加的Entry將與集合中原有Entry造成Entry鏈,並且新添加Entry位於Entry鏈的頭部。put源碼以下: 
public V put(K paramK, V paramV) {
        //若是key爲空,調用putForNullKey方法
        if (paramK == null)
            return putForNullKey(paramV);
        //根據key的keyCode計算Hash值
        int i = hash(paramK.hashCode());
        //搜索指定hash值的對應在table中的索引
        int j = indexFor(i, this.table.length);
        //若是j索引處的Entry不爲空,經過循環遍歷localEntry元素的下一個元素
        for (Entry localEntry = this.table[j]; localEntry != null; localEntry = localEntry.next) {
            Object localObject1;
            //找到指定key與放入key相等(hash值相同,經過equals比較返回true)
            if ((localEntry.hash == i)
                    && ((((localObject1 = localEntry.key) == paramK) || (paramK
                            .equals(localObject1))))) {
                Object localObject2 = localEntry.value;
                localEntry.value = paramV;
                localEntry.recordAccess(this);
                return localObject2;
            }
        }
        //若是j索引Entry爲null,此處沒有Entry
        this.modCount += 1;
        //將key、value添加到i索引處
        addEntry(i, paramK, paramV, j);
        return null;
    }

    void addEntry(int paramInt1, K paramK, V paramV, int paramInt2) {
        //獲取指定bucketIndex索引處Entry
        Entry localEntry = this.table[paramInt2];
        //將新建立的Entry放入bucketIndex索引處,並讓新的Entry指向原來的Entry
        this.table[paramInt2] = new Entry(paramInt1, paramK, paramV, localEntry);
        //若是map中的key-value數量超過
        if (this.size++ >= this.threshold)
        //table對象的長度擴充到2倍
            resize(2 * this.table.length);
    }

put方法三種狀況,如圖:性能

 

get()方法:當HashMap的每一個bucket裏存儲的Entry只是單個Entry,即沒有經過指針產生Entry鏈時,此時HashMap具備最好的性能。當程序經過key取出對應value時,系統先計算出該key的hashCode()返回值,再根據該hashCode返回值找出該key在table數組中的索引,而後取出該索引處的Entry,最後返回該key對應的value值。get源碼以下:this

public V get(Object paramObject) {
       //若是key爲空,調用getForNullKey取出對應的value
        if (paramObject == null)
            return getForNullKey();
        //根據key的hashCode值計算hash碼
        int i = hash(paramObject.hashCode());
        //直接取出table數組中指定索引處的值
        Entry localEntry = this.table[indexFor(i, this.table.length)];
        while (localEntry != null) {
            Object localObject;
            //若是該Entry的key與被搜索key相同
            if ((localEntry.hash == i)
                    && ((((localObject = localEntry.key) == paramObject) || (paramObject
                            .equals(localObject)))))
                return localEntry.value;
            //搜索該Entry鏈的下一個
            localEntry = localEntry.next;
        }

        return null;
    }
 從代碼看出,HashMap的每一個bucket裏只有一個Entry,HashMap能夠根據索引快速取出該bucket裏的Entry。
在發生Hash衝突的狀況下,單個bucket裏存儲的不是一個Entry,而是一個Entry鏈,系統只能按順序遍歷每一個Entry,直到找到想搜索的Entry。

HashMap有兩個參數影響其性能:spa

1. 初始容量和加載因子。默認初始容量是16,加載因子是0.75。容量是哈希表中桶(Entry數組)的數量,初始容量只是哈希表在建立時的容量。加載因子是哈希表在其容量自動增長以前能夠達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,經過調用rehash 方法將容量翻倍。線程

2. 加載因子太高雖然減小了空間開銷,但同時也增長了查詢成本(加載因子是表示Hsah表中元素的填滿的程度.若:加載因子越大,填滿的元素越多,好處是,空間利用率高了,但:衝突的機會加大了.反之,加載因子越小,填滿的元素越少,好處是:衝突的機會減少了,但:空間浪費多了)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地下降rehash 操做次數。若是初始容量大於最大條目數除以加載因子(實際上就是最大條目數小於初始容量*加載因子),則不會發生 rehash 操做。指針

3.HashMap存放的元素愈來愈多,到達臨界值(閥值)threshold時,就要對Entry數組擴容,這是Java集合類框架最大的魅力,HashMap在擴容時,新數組的容量將是原來的2倍,因爲容量發生變化,原有的每一個元素須要從新計算bucketIndex,再存放到新數組中去,也就是所謂的rehash。HashMap默認初始容量16,加載因子0.75,也就是說最多能放16*0.75=12個元素,當put第13個時,HashMap將發生rehash,rehash的一系列處理比較影響性能,因此當咱們須要向HashMap存放較多元素時,最好指定合適的初始容量和加載因子,不然HashMap默認只能存12個元素,將會發生屢次rehash操做。code

3、HashMap和Hashtable的區別對象

HashMap和Hashtable都實現了Map接口,主要的區別有:線程安全性,同步(synchronization),以及速度。HashMap幾乎能夠等價於Hashtable,除了HashMap是非synchronized的,並能夠接受null(HashMap能夠接受爲null的鍵值(key)和值(value),而Hashtable則不行)。

HashMap是非synchronized,而Hashtable是synchronized,意味着Hashtable是線程安全的,多個線程能夠共享一個Hashtable;而多個線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。另外一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器。 HashMap能夠經過下面的語句進行同步:Map m = Collections.synchronizeMap(hashMap);
 
4、HashMap和HashSet的區別
HashSet實現了Set接口,它不容許集合中有重複的值,HashMap實現了Map接口,Map接口對鍵值對進行映射。
HashSet擴展了HashMap,因此底層仍是用到map存儲,存儲實現同map一致,HashMap儲存鍵值,HashSet存儲對象。
相關文章
相關標籤/搜索