1. 程式人生 > 實用技巧 >探索HashSet底層實現

探索HashSet底層實現

前言

HashSet的底層實現依賴於HashMap,所以它的資料結構也是陣列 + 連結串列 + 紅黑樹,而對於它的類註釋也沒什麼好總結的,探索HashSet底層實現是基於JDK1.8。仔細一想,HashSet存在的意義是什麼?有時候需要新增元素時,也就是隻有單個物件,並沒有所謂的鍵值對,或許還有些用處,可這ArrayList也能做到啊!可是相比之下,HashSet由於有HashMap撐腰,它的效能要高於ArrayList,所以我認為HashSet是List和Map獨有的特性結合後的產物。

資料結構


    //可序列化、可克隆
    public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {

        //直接使用了HashMap來儲存它的元素
        private transient HashMap<E,Object> map;

        //既然用了HashMap就要考慮值應該存什麼,就是它了,不管新增的元素是什麼,它都作為值
        private static final Object PRESENT = new Object();
    }


建構函式


    /**
     * 初始化
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * 指定集合來初始化
     * @param c 指定集合
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
     * 指定初始容量與載入因子來初始化
     * @param initialCapacity 指定初始容量
     * @param loadFactor 指定載入因子
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     * 指定初始容量來初始化
     * @param initialCapacity 指定初始容量
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * 指定初始容量與載入因子來初始化
     * 對比上面這裡構造了LinkedHashMap,說明它是有序的
     * @param initialCapacity 指定初始容量
     * @param loadFactor 指定載入因子
     * @param dummy 無實際意義,為了與上面的建構函式區分開來
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

簡單方法


    /**
     * 獲取迭代器
     * @return 迭代器
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    /**
     * 獲取元素個數
     * @return 元素個數
     */
    public int size() {
        return map.size();
    }

    /**
     * HashSet是否為空
     * @return 是否為空
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * HashSet是否包含指定元素
     * @param o 指定元素
     * @return 是否包含指定元素
     */
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    /**
     * 新增元素
     * @param e 指定元素
     * @return 是否新增成功
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    /**
     * 移除指定元素
     * @param o 指定元素
     * @return 是否移除成功
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    /**
     * 清空
     */
    public void clear() {
        map.clear();
    }

    /**
     * 淺克隆
     */
    public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }


總結

  • HashSet無序、不可重複、非執行緒安全。

  • HashSet允許存放空元素。

  • HashSet底層基於HashMap。

  • 兩個相等的物件,即hashCode與equals都相等的情況,HashMap底層只是進行值替換,並未理睬鍵,所以就呈現了在往HashSet新增相等的物件時,只添加了第一個物件,第二個物件並未新增。

重點關注

基於HashMap