1. 程式人生 > >學習JDK8源碼之--HashSet

學習JDK8源碼之--HashSet

mat res ont iterator 參數 one 包括 ray trac

1. HashSet簡介

  HashSet是一個不可重復的無序集合,底層由HashMap實現存儲,故HashSet是非線程安全的,由於HashSet使用HashMap的Key來存儲元素,而HashMap的Key可以允許有一個null值,所以HashSet是可以存儲null值的。

  由於HashSet的不可重復性,在項目開發中可以利用這一點進行元素去重,但前提是在不考慮線程安全的問題基礎上。

2. HashSet的繼承關系

  HashSet繼承自AbstractSet,實現了Set、Cloneable、java.io.Serializable接口。

  AbstractSet繼承自AbstractCollection,實現了Set接口,此類並沒有重寫 AbstractCollection

類中的任何實現(包括add()方法)。它僅僅添加了 equalshashCode 的實現。

  實現了Set接口:表明本類是一個不包含重復元素的類,並最多包含一個null值。

  實現了Cloneable接口:可以調用Object.clone方法返回該對象的淺拷貝。

  實現了 java.io.Serializable 接口:可以啟用其序列化功能,能通過序列化去傳輸。

3. HashSet的實現

1. 核心屬性

    //HashMap存儲數據
    private transient HashMap<E,Object> map;
    //final修飾的不可改變的空對象
private static final Object PRESENT = new Object();

  底層通過HashMap來進行數據的存儲,HashMap的key來存儲元素,value統一存儲一個不可改變的空對象PRESENT,為什麽要這麽做呢?因為既然是通過HashMap存儲數據,數據的移除操作也是通過HashMap的remove方法進行移除,而HashMap的remove方法會返回被移除的Value值,而把PRESENT設為final就能保證可以通過判斷返回的值是否是PRESENT對象就可以判斷是否移除成功。

2. 構造函數

    //無參構造,實例化一個HashMap對象
public HashSet() { map = new HashMap<>(); } //帶參構造,傳入一個集合 public HashSet(Collection<? extends E> c) { //因為HashMap的默認加載因子是0.75,當元素個數達到容量的0.75倍的時候便會自動擴容 //所以,這裏取(int) (c.size()/.75f) + 1,為了避免實例化後的HashMap達到默認尺度 //因為HashMap的默認初始容量是16,所以取(int) (c.size()/.75f) + 1和16的最大值作為初始容量 map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } //帶參構造,傳入一個HashMap的初始容量 public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } //帶參構造,傳入HashMap的初始容量和加載因子 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } //帶參構造,傳入HashMap的初始容量和加載因子,dummy參數用來和上一個構造函數作區分,表明實例化一個LinkedHashMap HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }

3. 核心方法

    //添加一個元素
    public boolean add(E e) {
        //調用HashMap的put方法,因為HashMap的Key不能重復,重復時會把添加的元素直接返回,成功則返回null
        return map.put(e, PRESENT)==null;
    }

    //調用HashMap的clear方法來清空元素
    public void clear() {
        map.clear();
    }

    //調用HashMap的containsKey來判斷是否包含元素o
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    //通過調用HashMap的isEmpty方法來判斷集合是否為空
     public boolean isEmpty() {
        return map.isEmpty();
    }

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

    //調用HashMap的remove方法來移除元素
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    //HashMap的大小就是集合的大小
    public int size() {
        return map.size();
    }

  HashSet的基本元素操作都是通過調用HashMap的API來實現的,所以需要了解具體實現細節需要去學習HashMap。

4. HashSet的遍歷

1. 通過Iterator叠代器

// 假設set是HashSet對象
for(Iterator iterator = set.iterator();
       iterator.hasNext(); ) { 
    iterator.next();
} 

2. foreach

// 假設set是HashSet對象,並且set中元素是String類型
String[] arr = (String[])set.toArray(new String[0]);
for (String str:arr)
    System.out.println("for each : %s", str);

學習JDK8源碼之--HashSet