1. 程式人生 > >JAVA常用集合原始碼分析:HashSet

JAVA常用集合原始碼分析:HashSet

序言

在上一篇文章中,我們介紹了HashMap,其實本來想自己完成原始碼分析的一系列文章的,但是HashMap的原始碼著實是複雜,看的我腦殼疼。。於是就自己去找了找大牛們的文章反覆看,後面總算有了點門道了,大致知道了HashMap的原理,然後轉載了一篇我認為總結的比較好的文章到我的部落格裡,供大家一起學習。初步瞭解HashMap的原始碼後,自以為自己還OK了,於是便打算獨立把ConcurrentHashMap的原始碼也一併分析完,然後寫下來,可是一看程式碼行數

我。。。。給跪了orz   算了算了改天再來吧

HashSet

算了,回到正文,我們先來回想下HashSet是個啥?它也是個集合類(廢話...),可以儲存物件,但是儲存是無序的,不可以存取重複的物件...有了這些印象,我們就帶著問題來分析原始碼

  • 為什麼是無序
  • 為什麼不可以儲存重複物件

結構

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

沒有什麼特別的東西,很常用的繼承結構,一步一步慢慢分解下來,中間用抽象類來緩衝下面實現集合的工作量
實現的Set介面,這個是多餘的東西,可以不實現它,因為在AbstractSet中已經實現了set介面了,繼承了AbstractSet,就相當於也實現了Set介面
Cloneable

:能夠使用clone方法,目的是為了在陣列擴增大小的時候,能用到此方法
Serializable:能夠使之序列化

屬性

 private transient HashMap<E,Object> map;

 private static final Object PRESENT = new Object();

哇,巨簡單有木有!就兩個屬性!

根據這個屬性,我們也可以得出結論

HashSet的底層是由HashMap實現的

建構函式

    public HashSet() {
        map = new HashMap<>();
    }

    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

雖然建構函式有好幾個,但是都是initalCapacity(初始容量) loadFactor(負載因子)這些的組合而已,最後都是呼叫HashMap的構造方法對map進行初始化

常用方法

1.add(E)

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

這個方法是我們用的最多的方法,然鵝它的實現只有短短一行...這也進一步說明HashSet是靠底層的HashMap實現的。從這個方法中,我們可以發現set中的每一個值,都被當做一個key儲存在HashMap中,對應的value都是PRESENT,這也解釋了為什麼HashSet的值是不能重複的,因為map中的key是不能重複的,當有重複的值寫入到HashTree中,value值會被覆蓋,key值不受影響。

同樣,map中的值是無序的,所以TreeSet的值也是無序的啦。

2.remove(Object) 刪除一個物件

public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

同樣是呼叫map的刪除方法,至於其他的一些方法,也都大同小異,就不展開敘述啦

3.為什麼沒有get(index) 這個方法

因為是無序的呀!

4.那麼要如何遍歷呢?

        //增強for迴圈遍歷
        for(String s: set){
			System.out.println(s);
		}
		//迭代器遍歷
		Iterator<String> it = set.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}

總結

HashSet是我讀過最簡單的原始碼啦2333,畢竟複雜的操作都交給HashMap去實現啦,HashSet只要幾行程式碼呼叫下就ok,巨簡單

最後再回顧下HashSet的特點

  • 元素沒有順序(底層用的是HashMap,HashMap本身中的元素度沒有順序,那麼HashSet更不可能有了)
  • 元素不能重複(HashSet中存放的元素,度是當作HashMap的key來使用,HashMap中key是不能相同的,所以HashSet的元素也不能重複了)  
  • 非執行緒安全(HashMap有的問題它都有)