Java Set 常用集合 HashSet、LinkedHashSet、TreeSet



Java 中的 Set 是非常常用的數據類型。Set 是無序的 Collection,Java Set 有三個常用的實現類,分別是:HashSet、LinkedHashSet、TreeSet



本文基於 JDK8 分析


HashSet

HashSet 繼承自 AbstractSet,實現了 Set 接口。底層基於 HashMap 實現,是一個不允許有重復元素的無序集合。允許 null 元素,非線程安全。HashSet 還實現了 Cloneable、Serializable 接口,所以 HashSet 是支持復制、序列化的

所以說,HashMap 是替 HashSet 打工的。就像老板手下的員工,任勞任怨,做牛做馬,像極了被剝削的我們(小聲嗶嗶)

// 用於存儲元素的 HashMap
private transient HashMap<E,Object> map;
// 湊數的值元素,
private static final Object PRESENT = new Object();

HashSet 有五個構造函數,解釋下第二個構造函數:默認加載因子為 0.75 的情況下,假設 c 的元素個數就是 map 此時的最大閾值,最大閾值為 (int) (c.size()/.75f),再加一,通過 HashMap 的擴容機制(取大於當前容量的最小二次冪),就可以取得最適合的容量大小

// 構造一個默認容量為 16 的 HashMap
public HashSet()  {
    map = new HashMap<>();
}
// 將 Collection 中的元素賦給 HashMap
public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}
// 指定 HashMap 的初始容量和加載因子
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}
// 指定 HashMap 的初始容量
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}
// 供 LinkedHashSet 使用
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

HashSet 常用方法

第一個是 add 方法。HashSet 使用 HashMap 保證元素不重復,熟悉 HashMap 的都知道,HashMap 的 Key 是不允許重復的,所以可以把要添加的元素作為 HashMap 的 Key 保存,但 Value 還是要有的,所以 HashSet 又定義了一個靜態常量對象 PRESENT 來湊數,實際上並沒有什么意義

private static final Object PRESENT = new Object();

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

到這里就一目了然了,HashSet 中添加元素的方法其實就是調用 HashMap 的 put 方法,如果 put 方法的返回值為 null,證明以 e 為鍵的元素不存在,則可以添加;否則會把原有的值刪除並覆蓋,並返回原來的值。所以當 add 方法中的條件判斷成立,則證明添加成功,反之則失敗。如果不了解 HashMap 的機制,可以看一下下面這張圖

至於其他的 remove、contains 就更不用說了,全是 HashMap 的知識,不再贅述


LinkedHashSet

LinkedHashSet 是 HashSet 的子類,實現了 Set 接口,Set 有的特點它都有。既然 HashSet 靠 HashMap 干活,那是否 LinkedHashSet 也有自己的小弟呢?(沒錯,說的就是你 LinkedHashMap)

還記得之前提到在 HashSet 有一個專供 LinkedHashSet 使用的構造方法嗎?這個構造方法只能由 LinkedHashSet 調用,參數 dummy 並沒有實際意義,只是為了和 HashSet 中其他參數區分開罷了(重載原理)

LinkedHashMap 基於雙向鏈表實現,相比於 HashMap 最大的不同就是有序。LinkedHashSet 中除了四個構造器以外再無其他方法,全部繼承自 HashSet。如果想了解更多,就去看看 LinkedHashMap 吧

// HashSet 中專供 LinkedHashSet 使用的構造方法
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

// LinkedHashSet 的構造方法
public LinkedHashSet(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor, true);
}

public LinkedHashSet(int initialCapacity) {
    super(initialCapacity, .75f, true);
}

public LinkedHashSet() {
    super(16, .75f, true);
}

public LinkedHashSet(Collection<? extends E> c) {
    super(Math.max(2*c.size(), 11), .75f, true);
    addAll(c);
}

TreeSet

在此之前先了解一下 SortedSet,SortSet 擴展了 Set 並提供其元素的總排序,要求所有元素都必須實現 Comparable 接口,而且所有元素都必須是可比較的,即兩個對象可以互相作為 compareTo 方法的參數。從這里可以看出,SortedSet 所謂的有序並不是我們通常認為的先后插入順序,而是根據對象的比較函數對元素排序。SortSet 接口的方法如下:

// 返回用於對此集合中的元素進行排序的比較器,如果此集合使用其元素的自然順序,則返回 null
Comparator<? super E> comparator();
// 返回此集合的部分元素,元素范圍從 fromElement(包括)到 toElement(不包括)
SortedSet<E> subSet(E fromElement, E toElement);
// 返回此集合的部元素,其中元素全部小於 toElement
SortedSet<E> headSet(E toElement);
// 返回此集合的部分元素,其中元素全部大於或等於 fromElement
SortedSet<E> tailSet(E fromElement);
// 返回此集合中當前的第一個(最低)元素
E first();
// 返回此集合中當前的最后一個(最高)元素
E last();

NavigableSet 實現了 Sorted 接口,其本身也是一個接口,對 SortedHash 進行了擴展,支持導航方法,例如查找與指定目標最匹配項等。TreeSet 繼承自 AbstractSet,實現了 NavigableSet 接口。TreeSet 基於 TreeMap 實現,其構造方法如下:

private transient NavigableMap<E,Object> m;

// 構造一個指定的 NavigableMap 的集合
TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
}
// 默認方法,根據元素的自然排序進行排序
public TreeSet() {
    this(new TreeMap<E,Object>());
}
// 指定比較器進行排序
public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}
// 構造一個包含指定集合中元素的集合,根據元素的自然排序進行排序
public TreeSet(Collection<? extends E> c) {
    this();
    addAll(c);
}
// 構造一個包含相同元素的集合,並使用與指定排序集相同的排序
public TreeSet(SortedSet<E> s) {
    this(s.comparator());
    addAll(s);
}

TreeSet 也是基於 TreeMap 工作的,TreeMap 也是一個可排序的 Map,排序原理也是依靠比較器,更多的細節請了解 TreeMap



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM