Java 中的 Set 是很是經常使用的數據類型。Set 是無序的 Collection,Java Set 有三個經常使用的實現類,分別是:HashSet、LinkedHashSet、TreeSetjava
本文基於 JDK8 分析安全
HashSet 繼承自 AbstractSet,實現了 Set 接口。底層基於 HashMap 實現,是一個不容許有重複元素的無序集合。容許 null 元素,非線程安全。HashSet 還實現了 Cloneable、Serializable 接口,因此 HashSet 是支持複製、序列化的函數
因此說,HashMap 是替 HashSet 打工的。就像老闆手下的員工,不辭辛苦,作牛作馬,像極了被剝削的咱們(小聲嗶嗶)this
// 用於存儲元素的 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); }
第一個是 add 方法。HashSet 使用 HashMap 保證元素不重複,熟悉 HashMap 的都知道,HashMap 的 Key 是不容許重複的,因此能夠把要添加的元素做爲 HashMap 的 Key 保存,但 Value 仍是要有的,因此 HashSet 又定義了一個靜態常量對象 PRESENT 來湊數,實際上並無什麼意義code
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 的機制,能夠看一下下面這張圖對象
![](G:\SSS\Java\Java SE\博客\HashMap add.png)blog
至於其餘的 remove、contains 就更不用說了,全是 HashMap 的知識,再也不贅述排序
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); }
在此以前先了解一下 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