1 public class HashSet<E> 2 extends AbstractSet<E> 3 implements Set<E>, Cloneable, java.io.Serializable
咱們看到HashSet繼承了AbstractSet抽象類,並實現了Set、Cloneable、Serializable接口。AbstractSet是一個抽象類,對一些基礎的set操做進行封裝。繼續來看下Set接口的定義:html
1 public interface Set<E> extends Collection<E> { 2 // Query Operations 3 int size(); 4 boolean isEmpty(); 5 boolean contains(Object o); 6 Iterator<E> iterator(); 7 Object[] toArray(); 8 <T> T[] toArray(T[] a); 9 // Modification Operations 10 boolean add(E e); 11 boolean remove(Object o); 12 // Bulk Operations 13 boolean containsAll(Collection<?> c); 14 boolean addAll(Collection<? extends E> c); 15 boolean retainAll(Collection<?> c); 16 boolean removeAll(Collection<?> c); 17 void clear(); 18 // Comparison and hashing 19 boolean equals(Object o); 20 int hashCode(); 21 }
發現了什麼,Set接口和java.util.List接口同樣也實現了Collection接口,可是Set和List所不一樣的是,Set沒有get等跟下標先關的一些操做方法,那怎麼取值呢?Iterator還記得嗎,使用迭代器對不對。(不明白的回去看Iterator講解)java
2.底層存儲程序員
1 // 底層使用HashMap來保存HashSet的元素 2 private transient HashMap<E,Object> map; 3 4 // Dummy value to associate with an Object in the backing Map 5 // 因爲Set只使用到了HashMap的key,因此此處定義一個靜態的常量Object類,來充當HashMap的value 6 private static final Object PRESENT = new Object();
看到這裏就明白了,和咱們前面說的同樣,HashSet是用HashMap來保存數據,而主要使用到的就是HashMap的key。設計模式
看到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 {....},好爽。數組
3.構造方法post
1 /** 2 * 使用HashMap的默認容量大小16和默認加載因子0.75初始化map,構造一個HashSet 3 */ 4 public HashSet() { 5 map = new HashMap<E,Object>(); 6 } 7 8 /** 9 * 構造一個指定Collection參數的HashSet,這裏不只僅是Set,只要實現Collection接口的容器均可以 10 */ 11 public HashSet(Collection<? extends E> c) { 12 map = new HashMap<E,Object>(Math. max((int) (c.size()/.75f) + 1, 16)); 13 // 使用Collection實現的Iterator迭代器,將集合c的元素一個個加入HashSet中 14 addAll(c); 15 } 16 17 /** 18 * 使用指定的初始容量大小和加載因子初始化map,構造一個HashSet 19 */ 20 public HashSet( int initialCapacity, float loadFactor) { 21 map = new HashMap<E,Object>(initialCapacity, loadFactor); 22 } 23 24 /** 25 * 使用指定的初始容量大小和默認的加載因子0.75初始化map,構造一個HashSet 26 */ 27 public HashSet( int initialCapacity) { 28 map = new HashMap<E,Object>(initialCapacity); 29 } 30 31 /** 32 * 不對外公開的一個構造方法(默認default修飾),底層構造的是LinkedHashMap,dummy只是一個標示參數,無具體意義 33 */ 34 HashSet( int initialCapacity, float loadFactor, boolean dummy) { 35 map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); 36 }
從構造方法能夠很輕鬆的看出,HashSet的底層是一個HashMap,理解了HashMap後,這裏沒什麼可說的。只有最後一個構造方法有寫區別,這裏構造的是LinkedHashMap,該方法不對外公開,其實是提供給LinkedHashSet使用的,而第三個參數dummy是無心義的,只是爲了區分其餘構造方法。this
4.增長和刪除spa
/** * 利用HashMap的put方法實現add方法 */ public boolean add(E e) { return map .put(e, PRESENT)== null; } /** * 利用HashMap的remove方法實現remove方法 */ public boolean remove(Object o) { return map .remove(o)==PRESENT; } /** * 添加一個集合到HashSet中,該方法在AbstractCollection中 */ public boolean addAll(Collection<? extends E> c) { boolean modified = false; // 取得集合c迭代器Iterator Iterator<? extends E> e = c.iterator(); // 遍歷迭代器 while (e.hasNext()) { // 將集合c的每一個元素加入到HashSet中 if (add(e.next())) modified = true; } return modified; } /** * 刪除指定集合c中的全部元素,該方法在AbstractSet中 */ public boolean removeAll(Collection<?> c) { boolean modified = false; // 判斷當前HashSet元素個數和指定集合c的元素個數,目的是減小遍歷次數 if (size() > c.size()) { // 若是當前HashSet元素多,則遍歷集合c,將集合c中的元素一個個刪除 for (Iterator<?> i = c.iterator(); i.hasNext(); ) modified |= remove(i.next()); } else { // 若是集合c元素多,則遍歷當前HashSet,將集合c中包含的元素一個個刪除 for (Iterator<?> i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { i.remove(); modified = true; } } } return modified; }
5.是否包含設計
1 /** 2 * 利用HashMap的containsKey方法實現contains方法 3 */ 4 public boolean contains(Object o) { 5 return map .containsKey(o); 6 } 7 8 /** 9 * 檢查是否包含指定集合中全部元素,該方法在AbstractCollection中 10 */ 11 public boolean containsAll(Collection<?> c) { 12 // 取得集合c的迭代器Iterator 13 Iterator<?> e = c.iterator(); 14 // 遍歷迭代器,只要集合c中有一個元素不屬於當前HashSet,則返回false 15 while (e.hasNext()) 16 if (!contains(e.next())) 17 return false; 18 return true; 19 }
因爲HashMap基於hash表實現,hash表實現的容器最重要的一點就是能夠快速存取,那麼HashSet對於contains方法,利用HashMap的containsKey方法,效率是很是之快的。在我看來,這個方法也是HashSet最核心的賣點方法之一。指針
6.容量檢查
1 /** 2 * Returns the number of elements in this set (its cardinality). 3 * 4 * @return the number of elements in this set (its cardinality) 5 */ 6 public int size() { 7 return map .size(); 8 } 9 10 /** 11 * Returns <tt>true</tt> if this set contains no elements. 12 * 13 * @return <tt> true</tt> if this set contains no elements 14 */ 15 public boolean isEmpty() { 16 return map .isEmpty(); 17 }
1 /** 2 * Returns an iterator over the elements in this set. The elements 3 * are returned in no particular order. 4 * 5 * @return an Iterator over the elements in this set 6 * @see ConcurrentModificationException 7 */ 8 public Iterator<E> iterator() { 9 return map .keySet().iterator(); 10 }
我cha,咋回事,HashSet的iterator()方法居然也是利用HashMap實現的,咱們去看看HashMap的keySet()方法是什麼鬼。
1 public Set<K> keySet() { 2 Set<K> ks = keySet; 3 return (ks != null ? ks : (keySet = new KeySet())); 4 }
HashMap的keySet()方法的返回值居然是一個Set,具體實現是一個叫KeySet的東東,KeySet又是什麼鬼。
1 private final class KeySet extends AbstractSet<K> { 2 public Iterator<K> iterator() { 3 return newKeyIterator(); 4 } 5 public int size() { 6 return size ; 7 } 8 public boolean contains(Object o) { 9 return containsKey(o); 10 } 11 public boolean remove(Object o) { 12 return HashMap.this.removeEntryForKey(o) != null; 13 } 14 public void clear() { 15 HashMap. this.clear(); 16 } 17 }
哦,KeySet是一個實現了AbstractSet的HashMap的內部類。而KeySet的iterator()方法返回的是一個newKeyIterator()方法,好繞好繞,頭暈了。
1 Iterator<K> newKeyIterator() { 2 return new KeyIterator(); 3 }
newKeyIterator()方法返回的又是一個KeyIterator()方法,what are you 弄啥嘞?
1 private final class KeyIterator extends HashIterator<K> { 2 public K next() { 3 return nextEntry().getKey(); 4 } 5 }
好吧,不想說什麼了,繼續往下看吧。
1 private abstract class HashIterator<E> implements Iterator<E> { 2 // 下一個須要返回的節點 3 Entry<K,V> next; // next entry to return 4 int expectedModCount ; // For fast-fail 5 int index ; // current slot 6 // 當前須要返回的節點 7 Entry<K,V> current;// current entry 8 9 HashIterator() { 10 expectedModCount = modCount ; 11 if (size > 0) { // advance to first entry 12 Entry[] t = table; 13 // 初始化next參數,將next賦值爲HashMap底層的第一個不爲null節點 14 while (index < t.length && ( next = t[index ++]) == null) 15 ; 16 } 17 } 18 19 public final boolean hasNext() { 20 return next != null; 21 } 22 23 final Entry<K,V> nextEntry() { 24 if (modCount != expectedModCount) 25 throw new ConcurrentModificationException(); 26 // 取得HashMap底層數組中鏈表的一個節點 27 Entry<K,V> e = next; 28 if (e == null) 29 throw new NoSuchElementException(); 30 31 // 將next指向下一個節點,並判斷是否爲null 32 if ((next = e.next) == null) { 33 Entry[] t = table; 34 // 若是爲null,則遍歷真個數組,知道取得一個不爲null的節點 35 while (index < t.length && ( next = t[index ++]) == null) 36 ; 37 } 38 current = e; 39 // 返回當前節點 40 return e; 41 } 42 43 public void remove() { 44 if (current == null) 45 throw new IllegalStateException(); 46 if (modCount != expectedModCount) 47 throw new ConcurrentModificationException(); 48 Object k = current.key ; 49 current = null; 50 HashMap. this.removeEntryForKey(k); 51 expectedModCount = modCount ; 52 } 53 54 }
1 private abstract class LinkedHashIterator<T> implements Iterator<T> { 2 // header.after爲LinkedHashMap雙向鏈表的第一個節點,由於LinkedHashMap的header節點不保存數據 3 Entry<K,V> nextEntry = header .after; 4 // 最後一次返回的節點 5 Entry<K,V> lastReturned = null; 6 7 /** 8 * The modCount value that the iterator believes that the backing 9 * List should have. If this expectation is violated, the iterator 10 * has detected concurrent modification. 11 */ 12 int expectedModCount = modCount; 13 14 public boolean hasNext() { 15 return nextEntry != header; 16 } 17 18 public void remove() { 19 if (lastReturned == null) 20 throw new IllegalStateException(); 21 if (modCount != expectedModCount) 22 throw new ConcurrentModificationException(); 23 24 LinkedHashMap. this.remove(lastReturned .key); 25 lastReturned = null; 26 expectedModCount = modCount ; 27 } 28 29 Entry<K,V> nextEntry() { 30 if (modCount != expectedModCount) 31 throw new ConcurrentModificationException(); 32 if (nextEntry == header) 33 throw new NoSuchElementException(); 34 35 // 將要返回的節點nextEntry賦值給lastReturned 36 // 將nextEntry賦值給臨時變量e(由於接下來nextEntry要指向下一個節點) 37 Entry<K,V> e = lastReturned = nextEntry ; 38 // 將nextEntry指向下一個節點 39 nextEntry = e.after ; 40 // 放回當前需返回的節點 41 return e; 42 } 43 }