Table of Contents
HashSet
概述
-
HashSet 是基於 HashMap 實現的,HashSet 底層採用 HashMap 來保存所有元素
-
所有放入 HashSet 中的集合元素實際上由 HashMap 的 key 來保存,而 HashMap 的 value 則存儲了一個 PRESENT,它是一個靜態的 Object 對象。
-
HashSet 的絕大部分方法都是通過調用 HashMap 的方法來實現的,因此 HashSet 和 HashMap 兩個集合在實現本質上是相同的。 我們應該爲保存到 HashSet 中的對象覆蓋 hashCode() 和 equals() 方法
我們先通過 HashSet 最簡單的構造函數和幾個成員變量來看一下,證明其底層是 HashMap:
1 2 3 4 5 6 7 8 9 10 11 12
|
private transient HashMap<E,Object> map; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); }
|
默認情況下采用的是 initial capacity爲16,load factor 爲 0.75。
構造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
/** * 默認的無參構造器,構造一個空的HashSet。 * * 實際底層會初始化一個空的HashMap,並使用默認初始容量爲16和加載因子0.75。 */ public HashSet() { map = new HashMap<E,Object>(); } /** * 構造一個包含指定collection中的元素的新set。 * * 實際底層使用默認的加載因子0.75和足以包含指定collection中所有元素的初始容量來創建一個HashMap。 * @param c 其中的元素將存放在此set中的collection。 */ public HashSet(Collection<? extends E> c) { map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * 以指定的initialCapacity和loadFactor構造一個空的HashSet。 * * 實際底層以相應的參數構造一個空的HashMap。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); } /** * 以指定的initialCapacity構造一個空的HashSet。 * * 實際底層以相應的參數及加載因子loadFactor爲0.75構造一個空的HashMap。 * @param initialCapacity 初始容量。 */ public HashSet(int initialCapacity) { map = new HashMap<E,Object>(initialCapacity); } /** * 以指定的initialCapacity和loadFactor構造一個新的空鏈接哈希集合。此構造函數爲包訪問權限,不對外公開, * 實際只是是對LinkedHashSet的支持。 * * 實際底層會以指定的參數構造一個空LinkedHashMap實例來實現。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 * @param dummy 標記。 */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); }
|
add方法
1 2 3 4 5 6 7 8
|
/** * @param e 將添加到此set中的元素。 * @return 如果此set尚未包含指定元素,則返回true。 */ public boolean add(E e) { return map.put(e, PRESENT)==null; }
|
由於 HashMap 的 put() 方法添加 key-value 對時,當新放入 HashMap 的 Entry 中 key 與集合中原有 Entry 的 key 相同(hashCode()返回值相等,通過 equals 比較也返回 true),新添加的 Entry 的 value 會將覆蓋原來 Entry 的 value(HashSet 中的 value 都是PRESENT),但key不會有任何改變。
該方法如果添加的是在 HashSet 中不存在的,則返回 true;如果添加的元素已經存在,返回 false。其原因在於我們之前提到的關於 HashMap 的 put 方法。該方法在添加 key 不重複的鍵值對的時候,會返回 null。
contains方法
1 2 3 4 5 6 7 8 9 10 11
|
/** * 如果此set包含指定元素,則返回true。 * 更確切地講,當且僅當此set包含一個滿足(o==null ? e==null : o.equals(e))的e元素時,返回true。 * * 底層實際調用HashMap的containsKey判斷是否包含指定key。 * @param o 在此set中的存在已得到測試的元素。 * @return 如果此set包含指定元素,則返回true。 */ public boolean contains(Object o) { return map.containsKey(o); }
|
remove方法
/**
* 如果指定元素存在於此set中,則將其移除。更確切地講,如果此set包含一個滿足(o==null ? e==null : o.equals(e))的元素e,
* 則將其移除。如果此set已包含該元素,則返回true
*
* 底層實際調用HashMap的remove方法刪除指定Entry。
* @param o 如果存在於此set中則需要將其移除的對象。
* @return 如果set包含指定元素,則返回true。
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
clone方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
/** * 返回此HashSet實例的淺表副本:並沒有複製這些元素本身。 * * 底層實際調用HashMap的clone()方法,獲取HashMap的淺表副本,並設置到HashSet中。 */ 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(); } }
|
注意
- 對於 HashSet 中保存的對象,請注意正確重寫其 equals 和 hashCode 方法,以保證放入的對象的唯一性。這兩個方法是比較重要的,希望大家在以後的開發過程中需要注意一下
LinkedHashSet
概述
LinkedHashSet 首先我們需要知道的是它是一個 Set 的實現,所以它其中存的肯定不是鍵值對,而是值。此實現與 HashSet 的不同之處在於,LinkedHashSet 維護着一個運行於所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序可爲插入順序或是訪問順序。
LinkedHashSet 底層使用 LinkedHashMap 來保存所有元素,它繼承與 HashSet,其所有的方法操作上又與 HashSet 相同,因此 LinkedHashSet 的實現上非常簡單,只提供了四個構造方法,並通過傳遞一個標識參數,調用父類的構造器,底層構造一個 LinkedHashMap 來實現,在相關操作上與父類 HashSet 的操作相同,直接調用父類 HashSet 的方法即可。LinkedHashSet 的源代碼如下:
構造方法
1 2 3 4 5 6 7 8 9 10 11 12
|
/** * 以指定的initialCapacity和loadFactor構造一個新的空鏈接哈希集合。 * 此構造函數爲包訪問權限,不對外公開,實際只是是對LinkedHashSet的支持。 * * 實際底層會以指定的參數構造一個空LinkedHashMap實例來實現。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 * @param dummy 標記。 */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); }
|
LinkedHashSet 通過繼承 HashSet,底層使用 LinkedHashMap,以很簡單明瞭的方式來實現了其自身的所有功能。
其他代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { private static final long serialVersionUID = -2851667679971038690L; /** * 構造一個帶有指定初始容量和加載因子的新空鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個有指定初始容量和加載因子的LinkedHashMap實例。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 */ public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } /** * 構造一個帶指定初始容量和默認加載因子0.75的新空鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個帶指定初始容量和默認加載因子0.75的LinkedHashMap實例。 * @param initialCapacity 初始容量。 */ public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } /** * 構造一個帶默認初始容量16和加載因子0.75的新空鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個帶默認初始容量16和加載因子0.75的LinkedHashMap實例。 */ public LinkedHashSet() { super(16, .75f, true); } /** * 構造一個與指定collection中的元素相同的新鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個足以包含指定collection * 中所有元素的初始容量和加載因子爲0.75的LinkedHashMap實例。 * @param c 其中的元素將存放在此set中的collection。 */ public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); } }
|
總結
- LinkedHashSet 是 Set 的一個具體實現,其維護着一個運行於所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序可爲插入順序或是訪問順序。
- LinkedHashSet 繼承與 HashSet,並且其內部是通過 LinkedHashMap 來實現的。有點類似於我們之前說的LinkedHashMap 其內部是基於 Hashmap 實現一樣,不過還是有一點點區別的(具體的區別大家可以自己去思考一下)
- 如果我們需要迭代的順序爲插入順序或者訪問順序,那麼 LinkedHashSet 是需要你首先考慮的
參考:
- http://alex09.iteye.com/blog/539549
- http://wiki.jikexueyuan.com/project/java-collection/hashset.html
- http://wiki.jikexueyuan.com/project/java-collection/linkedhashset.html
Table of Contents
HashSet
概述
-
HashSet 是基於 HashMap 實現的,HashSet 底層採用 HashMap 來保存所有元素
-
所有放入 HashSet 中的集合元素實際上由 HashMap 的 key 來保存,而 HashMap 的 value 則存儲了一個 PRESENT,它是一個靜態的 Object 對象。
-
HashSet 的絕大部分方法都是通過調用 HashMap 的方法來實現的,因此 HashSet 和 HashMap 兩個集合在實現本質上是相同的。 我們應該爲保存到 HashSet 中的對象覆蓋 hashCode() 和 equals() 方法
我們先通過 HashSet 最簡單的構造函數和幾個成員變量來看一下,證明其底層是 HashMap:
1 2 3 4 5 6 7 8 9 10 11 12
|
private transient HashMap<E,Object> map; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); }
|
默認情況下采用的是 initial capacity爲16,load factor 爲 0.75。
構造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
/** * 默認的無參構造器,構造一個空的HashSet。 * * 實際底層會初始化一個空的HashMap,並使用默認初始容量爲16和加載因子0.75。 */ public HashSet() { map = new HashMap<E,Object>(); } /** * 構造一個包含指定collection中的元素的新set。 * * 實際底層使用默認的加載因子0.75和足以包含指定collection中所有元素的初始容量來創建一個HashMap。 * @param c 其中的元素將存放在此set中的collection。 */ public HashSet(Collection<? extends E> c) { map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * 以指定的initialCapacity和loadFactor構造一個空的HashSet。 * * 實際底層以相應的參數構造一個空的HashMap。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); } /** * 以指定的initialCapacity構造一個空的HashSet。 * * 實際底層以相應的參數及加載因子loadFactor爲0.75構造一個空的HashMap。 * @param initialCapacity 初始容量。 */ public HashSet(int initialCapacity) { map = new HashMap<E,Object>(initialCapacity); } /** * 以指定的initialCapacity和loadFactor構造一個新的空鏈接哈希集合。此構造函數爲包訪問權限,不對外公開, * 實際只是是對LinkedHashSet的支持。 * * 實際底層會以指定的參數構造一個空LinkedHashMap實例來實現。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 * @param dummy 標記。 */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); }
|
add方法
1 2 3 4 5 6 7 8
|
/** * @param e 將添加到此set中的元素。 * @return 如果此set尚未包含指定元素,則返回true。 */ public boolean add(E e) { return map.put(e, PRESENT)==null; }
|
由於 HashMap 的 put() 方法添加 key-value 對時,當新放入 HashMap 的 Entry 中 key 與集合中原有 Entry 的 key 相同(hashCode()返回值相等,通過 equals 比較也返回 true),新添加的 Entry 的 value 會將覆蓋原來 Entry 的 value(HashSet 中的 value 都是PRESENT),但key不會有任何改變。
該方法如果添加的是在 HashSet 中不存在的,則返回 true;如果添加的元素已經存在,返回 false。其原因在於我們之前提到的關於 HashMap 的 put 方法。該方法在添加 key 不重複的鍵值對的時候,會返回 null。
contains方法
1 2 3 4 5 6 7 8 9 10 11
|
/** * 如果此set包含指定元素,則返回true。 * 更確切地講,當且僅當此set包含一個滿足(o==null ? e==null : o.equals(e))的e元素時,返回true。 * * 底層實際調用HashMap的containsKey判斷是否包含指定key。 * @param o 在此set中的存在已得到測試的元素。 * @return 如果此set包含指定元素,則返回true。 */ public boolean contains(Object o) { return map.containsKey(o); }
|
remove方法
/**
* 如果指定元素存在於此set中,則將其移除。更確切地講,如果此set包含一個滿足(o==null ? e==null : o.equals(e))的元素e,
* 則將其移除。如果此set已包含該元素,則返回true
*
* 底層實際調用HashMap的remove方法刪除指定Entry。
* @param o 如果存在於此set中則需要將其移除的對象。
* @return 如果set包含指定元素,則返回true。
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
clone方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
/** * 返回此HashSet實例的淺表副本:並沒有複製這些元素本身。 * * 底層實際調用HashMap的clone()方法,獲取HashMap的淺表副本,並設置到HashSet中。 */ 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(); } }
|
注意
- 對於 HashSet 中保存的對象,請注意正確重寫其 equals 和 hashCode 方法,以保證放入的對象的唯一性。這兩個方法是比較重要的,希望大家在以後的開發過程中需要注意一下
LinkedHashSet
概述
LinkedHashSet 首先我們需要知道的是它是一個 Set 的實現,所以它其中存的肯定不是鍵值對,而是值。此實現與 HashSet 的不同之處在於,LinkedHashSet 維護着一個運行於所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序可爲插入順序或是訪問順序。
LinkedHashSet 底層使用 LinkedHashMap 來保存所有元素,它繼承與 HashSet,其所有的方法操作上又與 HashSet 相同,因此 LinkedHashSet 的實現上非常簡單,只提供了四個構造方法,並通過傳遞一個標識參數,調用父類的構造器,底層構造一個 LinkedHashMap 來實現,在相關操作上與父類 HashSet 的操作相同,直接調用父類 HashSet 的方法即可。LinkedHashSet 的源代碼如下:
構造方法
1 2 3 4 5 6 7 8 9 10 11 12
|
/** * 以指定的initialCapacity和loadFactor構造一個新的空鏈接哈希集合。 * 此構造函數爲包訪問權限,不對外公開,實際只是是對LinkedHashSet的支持。 * * 實際底層會以指定的參數構造一個空LinkedHashMap實例來實現。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 * @param dummy 標記。 */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); }
|
LinkedHashSet 通過繼承 HashSet,底層使用 LinkedHashMap,以很簡單明瞭的方式來實現了其自身的所有功能。
其他代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { private static final long serialVersionUID = -2851667679971038690L; /** * 構造一個帶有指定初始容量和加載因子的新空鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個有指定初始容量和加載因子的LinkedHashMap實例。 * @param initialCapacity 初始容量。 * @param loadFactor 加載因子。 */ public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } /** * 構造一個帶指定初始容量和默認加載因子0.75的新空鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個帶指定初始容量和默認加載因子0.75的LinkedHashMap實例。 * @param initialCapacity 初始容量。 */ public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } /** * 構造一個帶默認初始容量16和加載因子0.75的新空鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個帶默認初始容量16和加載因子0.75的LinkedHashMap實例。 */ public LinkedHashSet() { super(16, .75f, true); } /** * 構造一個與指定collection中的元素相同的新鏈接哈希set。 * * 底層會調用父類的構造方法,構造一個足以包含指定collection * 中所有元素的初始容量和加載因子爲0.75的LinkedHashMap實例。 * @param c 其中的元素將存放在此set中的collection。 */ public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); } }
|
總結
- LinkedHashSet 是 Set 的一個具體實現,其維護着一個運行於所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序可爲插入順序或是訪問順序。
- LinkedHashSet 繼承與 HashSet,並且其內部是通過 LinkedHashMap 來實現的。有點類似於我們之前說的LinkedHashMap 其內部是基於 Hashmap 實現一樣,不過還是有一點點區別的(具體的區別大家可以自己去思考一下)
- 如果我們需要迭代的順序爲插入順序或者訪問順序,那麼 LinkedHashSet 是需要你首先考慮的
參考: