1. 程式人生 > >java 集合 —— Set 實現類之 HashSet

java 集合 —— Set 實現類之 HashSet

原始碼基於jdk 1.7.81

HashSet 簡介

HashSet 是一個元素不能重複的集合。

HashSet 中當新增的元素有重複時,新增失敗。

HashSet 是 Set 的一個實現類,而 Set 又繼承了Collection 方法,並且沒有新增多餘的方法。

HashSet 繼承了AbstractSet 類。

實現了 Cloneable 介面,說明它重寫了clone()方法,可以被克隆。

實現了 java.io.Serializable 介面,說明它可以被序列化。

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

HashSet 成員變數

    static final long serialVersionUID = -5024744406713321676L;
	
    private transient HashMap<E,Object> map;

    private static final Object PRESENT = new Object();

 map 儲存的是鍵值對,在這裡我們只用到了它的 key ,Object 是用來給它的 value 賦值的。所以 HashSet 擁有 HashMap 的所有特性,是用陣列加連結串列的資料結構,並且每個 key 值是唯一的,在 HashSet 裡體現的是它的值使唯一的。

HashSet 的建構函式

	//預設建構函式
    public HashSet() {
		map = new HashMap<E,Object>();
    }

	//儲存Collection 裡面所有的資料
    public HashSet(Collection<? extends E> c) {
    	  // 建立map。
          // 為什麼要呼叫Math.max((int) (c.size()/.75f) + 1, 16),從 (c.size()/.75f) + 1 和 16 中選擇一個比較大的樹呢?        
          // 首先,說明(c.size()/.75f) + 1
          //   因為從HashMap的效率(時間成本和空間成本)考慮,HashMap的載入因子是0.75。
          //   當HashMap的“閾值”(閾值=HashMap總的大小*載入因子) < “HashMap實際大小”時,
          //   就需要將HashMap的容量翻倍。
          //   所以,(c.size()/.75f) + 1 計算出來的正好是總的空間大小。
          // 接下來,說明為什麼是 16 。
          //   HashMap的總的大小,必須是2的指數倍。若建立HashMap時,指定的大小不是2的指數倍;
          //   HashMap的建構函式中也會重新計算,找出比“指定大小”大的最小的2的指數倍的數。
          //   所以,這裡指定為16是從效能考慮。避免重複計算。
		map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
		 //增加 c 的元素在 map 裡。
		addAll(c);
    }

	//自動義容量和載入因子。
    public HashSet(int initialCapacity, float loadFactor) {
		map = new HashMap<E,Object>(initialCapacity, loadFactor);
    }
	
	//自定義容量
    public HashSet(int initialCapacity) {
		map = new HashMap<E,Object>(initialCapacity);
    }
	
	//自動義容量和載入因子。
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
		map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
    }

HashSet 的構造方法都是呼叫 HashMap 的構造方法,學習HashSet 的時候可以對照著HashMap 來學習。

HashSet 的迭代器

//返回HashSet的迭代器
    public Iterator<E> iterator() {
		return map.keySet().iterator();
    }

HashSet 的迭代器是呼叫了HashMap 通過鍵遍歷的迭代器。

HashSet 的基本操作

     /*
     * 不能重複
     * 去重
     * key 允許為 null
     * 執行緒不安全
     */
    public boolean add(E e) {
		return map.put(e, PRESENT)==null;//呼叫了HashMap的add方法
    }

    public V put(K key, V value) {
    	//當 key 為 null
        if (key == null)
            return putForNullKey(value);
		//得到 hash 值
        int hash = hash(key.hashCode());
		//獲得索引
        int i = indexFor(hash, table.length);
		//遍歷索引位置的連結串列
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
			//如果連結串列中已經存在當前的key,覆蓋即可
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
		//如果當前連結串列沒有就增加這個鍵值對到集合中
        addEntry(hash, key, value, i);
        return null;    // 當原來沒有這個元素時,返回null.
    }

當被新增的元素已經存在在集合中,put 方法返回的 是原來的 value 值,在add 方法中,put 方法返回的 oldvalue 值 != null ,返回false,則新增失敗。

當被新增的元素不存在集合中,新增元素在map 裡,返回 null。