1. 程式人生 > >java源碼閱讀HashSet

java源碼閱讀HashSet

ray valid get bool return 不變 implement checked nts

1類簽名與註解

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

此類實現Set接口,由哈希表(實際為HashMap實例)支持。 對集合的叠代次序不作任何保證。特別是,它不能保證順序在一段時間內保持不變(HashMap的擴容重hash)。 這個類允許null元素。

請註意,此實現不同步。 如果多個線程並發訪問哈希集,並且至少有一個線程修改該集合,那麽它必須在外部進行同步。 這通常通過在自然地封裝集合的一些對象上進行同步來實現。 如果沒有這樣的對象存在,那麽該集合應該使用Collections.synchronizedSet

方法“包裝”。 這最好在創建時完成,以防止對該集合的意外不同步訪問:

 Set s = Collections.synchronizedSet(new HashSet(...)); 

該類iterator方法返回的叠代器是故障快速的。此機制在HashMap一節中有詳細講述。

2屬性

static final long serialVersionUID = -5024744406713321676L;

private transient HashMap<E,Object> map;

// Map中的一個虛擬值
private static
final Object PRESENT = new Object();

HashSet是通過HashMap實現的,所以內部持有map的引用,Set的值對應著Map中的key,但是每次map的插入需要是<key,value>的鍵值對,所以就有了虛擬的value對象,就是PRESENT。

3構造方法

//1默認
public HashSet() {
        map = new HashMap<>();
    }

//2通過集合構造
public HashSet(Collection<? extends E> c) {
        map 
= new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } //3指定HashMap的初始化容量和負載因子 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } //4指定HashMap的初始化容量 public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } //5指定初始化容量和負載因子,內部通過LinkedHashMap實現 HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }

註意:構造方法5的dummy參數其實沒有特別的意義,唯一作用是通過多一個參數來區別構造方法3(方法重載)。

4常用方法

(1)add

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

(2)remove

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

(3)contains

public boolean contains(Object o) {
        return map.containsKey(o);
    }

(4)其他常用

public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

public int size() {
        return map.size();
    }

public boolean isEmpty() {
        return map.isEmpty();
    }

HashSet的常用方法都是通過調用HashMap的方法實現的。

5其他

(1)clone

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);
        }
    }

(2)序列化與反序列化

//序列化
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out HashMap capacity and load factor
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        s.writeInt(map.size());

        // Write out all elements in the proper order.
        for (E e : map.keySet())
            s.writeObject(e);
    }

//反序列化  
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // 讀capacity(檢查非負).
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " + capacity);
        }

        // 讀負載因子(不能為null).
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " + loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " + size);
        }
        // 計算需要的capacity
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // Constructing the backing map will lazily create an array when the first element is
        // added, so check it before construction. Call HashMap.tableSizeFor to compute the
        // actual allocation size. Check Map.Entry[].class since it‘s the nearest public type to
        // what is actually created.

        SharedSecrets.getJavaOISAccess()
                     .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));

        // 構造HashMap對象
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // 往HashMap中添加鍵值對
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }

(3)equals

HashSet類中沒有實現equls方法,而是從父類AbstractSet中繼承過來的。AbstractSet中equls實現如下:

public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }

public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }

equals首先判斷是否引用同1個對象,若是則返回true。

否則,判斷是否都是Set類型的,若不是則返回false。

若是,判斷是否都有相等的size,若不是則返回false。

若是,則調用containsAll是否包含所有元素,若是則返回true,否則返回false。

java源碼閱讀HashSet