Java集合框架分析(五)-HashSet分析

本篇文章主要分析一下Java集合框架中的Set部分,HashSet,該源碼分析基於JDK1.8,分析工具,AndroidStudio,文章分析不足之處,還請指正!java

HashSet簡介

類結構

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

HashSet是一個沒有重複元素的集合 。它是由HashMap實現的, 不保證元素的順序 ,並且 HashSet容許使用 null 元素 。HashSet是 非同步的 。和List接口同樣,HashSet也是先繼承了AbstractSet同時實現了Set接口,實現了Cloneable接口,即覆蓋了函數clone(),能克隆。實現java.io.Serializable接口,這意味着HashSet支持序列化,能經過序列化去傳輸。程序員

屬性

接下來咱們詳細分析一下HashSet相關源碼。首先咱們來看下它的屬性。bash

static final long serialVersionUID = -5024744406713321676L;
// 底層使用HashMap來保存HashSet的元素
   private transient HashMap<E,Object> map;
   // 因爲Set只使用到了HashMap的key,因此此處定義一個靜態的常量Object類,來充當HashMap的value
   private static final Object PRESENT = new Object();
複製代碼

是否是很奇怪?HashSet的內部居然使用了HashMap的數據的數據結構,這就有點意思了,咱們都知道Set這種數據結構是不容許有重複的存在的,接下來咱們就一探究竟,看看到底如何實現不重複操做的。數據結構

構造器

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);
   }
複製代碼

上面即是HashSet的構造器,很是簡單,因爲底層是使用了HashMap的數據結構,因此它的構造器就是初始化HashMap的代碼,只有最後一個構造方法有寫區別,這裏構造的是LinkedHashMap,該方法不對外公開,其實是提供給LinkedHashSet使用的,而第三個參數dummy是無心義的,只是爲了區分其餘構造方法。框架

其餘方法

咱們分下一下HashSet的添加add方法。函數

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

看到了什麼?很是簡單,就是HashMap的put方法,可是發現沒有,它的value是PRESENT,是咱們一開始申明的Object對象。工具

private static final Object PRESENT = new Object();
複製代碼

在這裏咱們就須要着重說明一下了,爲何要這樣搞?源碼分析

看到private static final Object PRESENT = new Object();不知道你有沒有一點疑問呢。這裏使用一個靜態的常量Object類來充當HashMap的value,既然這裏map的value是沒有意義的,爲何不直接使用null值來充當value呢?好比寫成這樣子private final Object PRESENT = null;咱們都知道的是,Java首先將變量PRESENT分配在棧空間,而將new出來的Object分配到堆空間,這裏的new Object()是佔用堆內存的(一個空的Object對象佔用8byte),而null值咱們知道,是不會在堆空間分配內存的。那麼想想這裏爲何不使用null值。想到什麼嗎,看一個異常類java.lang.NullPointerException,這絕對是Java程序員的一個噩夢,這是全部Java程序猿都會遇到的一個異常,你看到這個異常你覺得很好解決,可是有些時候也不是那麼容易解決,Java號稱沒有指針,可是到處碰到NullPointerException。因此啊,爲了從根源上避免NullPointerException的出現,浪費8個byte又怎麼樣,在下面的代碼中我不再會寫這樣的代碼啦if (xxx == null) { … } else {….},好爽。學習

咱們接着分析一下其餘的方法。ui

public boolean remove(Object o) {
       return map.remove(o)==PRESENT;
   }
複製代碼

移除功能也就是調用HashMap的移除功能,沒什麼好說的,對於熟悉HashMap源碼的夥伴能夠參考文章開頭列出來的一系列的文章。

因爲HashSet的源碼實在是比較簡單,因此一次性都把剩餘的方法都簡單註釋一下吧。

迭代器

//迭代器
  public Iterator<E> iterator() {
      return map.keySet().iterator();
  }
複製代碼

容量

public int size() {
      return map.size();
  }
複製代碼

**是否爲空集合 **

//是否爲空集合 
  public boolean isEmpty() {
      return map.isEmpty();
  }
複製代碼

是否包含一個元素

//是否包含一個元素
  public boolean contains(Object o) {
      return map.containsKey(o);
  }
複製代碼

以上即是HashSet的大概源碼,HashSet的源碼比較簡單,主要是依靠HashMap來實現的,接下來咱們總結一下關於HashSet的一些內容。

總結

因爲HashMap基於hash表實現,hash表實現的容器最重要的一點就是能夠快速存取,那麼HashSet對於contains方法,利用HashMap的containsKey方法,效率是很是之快的。 HashSet 是一個沒有重複元素的集合。它是由HashMap實現的,不保證元素的順序,並且HashSet容許使用 null 元素,HashSet是非同步的。

關於做者

專一於 Android 開發多年,喜歡寫 blog 記錄總結學習經驗,blog 同步更新於本人的公衆號,歡迎你們關注,一塊兒交流學習~

在這裏插入圖片描述
相關文章
相關標籤/搜索