Map篇暫告段落,卻並不是離咱們而去。這不在本篇中你就能常常見到她。HashSet、LinkedHashSet、TreeSet各自基於對應Map實現,各自源碼內容較少,所以概括爲一篇。php
// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();
HashSet基於HashMap實現;而Map是鍵值對形式的,所以構造一個PRESENT僞裝爲值。html
一樣在HashSet源碼的Line273與Line294分別見看到老朋友writeObject()和readObject()。使用它們自定義序列化規則,將不會調用默認的序列化方法。java
這樣作能夠下降性能消耗的同時,還能夠減小序列化字節流的大小,從而減小網絡開銷(RPC框架中)。[①]api
記得在以前的文章中留了一個問題。即該private方法供誰調用?解釋以下,然而筆者並未在ObjectOutputStream源碼中找到getPrivateMethod方法,不知是否因爲版本不一樣仍是做者筆誤。卻是在ObjectStreamClass中找到了getPrivateMethod()。安全
ObjectOutputStream使用了反射來尋找是否聲明瞭這兩個方法。由於ObjectOutputStream使用getPrivateMethod,因此這些方法不得不被聲明爲priate以致於供ObjectOutputStream來使用。 [②]網絡
/** * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> * and <em>fail-fast</em> {@link Spliterator} over the elements in this * set. * * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and * {@link Spliterator#DISTINCT}. Overriding implementations should document * the reporting of additional characteristic values. * * @return a {@code Spliterator} over the elements in this set * @since 1.8 */ public Spliterator<E> spliterator() { return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0); } // HashMap源碼中 static final class KeySpliterator<K,V> extends HashMapSpliterator<K,V> implements Spliterator<K> { KeySpliterator(HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public KeySpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid || current != null) ? null : new KeySpliterator<>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super K> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); HashMap<K,V> m = map; Node<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = (tab == null) ? 0 : tab.length; } else mc = expectedModCount; if (tab != null && tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { Node<K,V> p = current; current = null; do { if (p == null) p = tab[i++]; else { action.accept(p.key); p = p.next; } } while (p != null || i < hi); if (m.modCount != mc) throw new ConcurrentModificationException(); } } public boolean tryAdvance(Consumer<? super K> action) { int hi; if (action == null) throw new NullPointerException(); Node<K,V>[] tab = map.table; if (tab != null && tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { K k = current.key; current = current.next; action.accept(k); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } return false; } public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT; } }
spliterator()將Set中全部元素封裝中並返回,依靠HashMap.KeySpliterator()方法來實現。HashMap.KeySpliterator重寫了Spliterator接口的一些方法:數據結構
tryAdvance:若是存在沒處理(action.accept(k))的數據,執行指定的代碼並返回true;若不存在,直接返回false;單次;框架
forEachRemaining:循環對全部數據進行處理(action.accept(p.key));less
trySplit:分割出一個新的Spliterator,從「mid = (lo + hi) >>> 1;」來看,KeySpliterator是對半切割的。性能
characteristics:返回特徵值。這裏只會有2種結果。Spliterator.SIZED(0x00000040)|Spliterator.DISTINCT(0x00000001)=65(十進制)和0|Spliterator.DISTINCT(0x00000001)=1,經過返回值能反應KeySpliterator當前狀態。由於一旦調用以上方法處理數據,fence值就會被改變,即從65變爲1(我的理解,網上資料百裏挑一)。
「jdk1.8中的集合框架中的數據結構都默認實現了Spliterator。」(慚愧的是當時在看HashMap並無注意到,因爲Set代碼行數少,反倒引發了關注。)看看下面的執行結果你是否能所有bingo呢?
HashSet hs = new HashSet(); hs.add("c"); hs.add("h"); hs.add("e"); Spliterator<String> spliterator = hs.spliterator(); System.out.println("characteristics:"+spliterator.characteristics()); Spliterator<String> spliterator2 = spliterator.trySplit(); while(spliterator.tryAdvance(t -> System.out.println("tryAdvance:"+t.toString()))); while(spliterator2.tryAdvance(t -> System.out.println("trySplit:"+t.toString()))); System.out.println("characteristics:"+spliterator.characteristics()); hs.spliterator().forEachRemaining(t -> System.out.println("forEachRemaining:"+t.toString()));
/** * Constructs a new, empty linked hash set. (This package private * constructor is only used by LinkedHashSet.) The backing * HashMap instance is a LinkedHashMap with the specified initial * capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hash map * @param loadFactor the load factor of the hash map * @param dummy ignored (distinguishes this * constructor from other int, float constructor.) * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
增長dummy標誌與HashSet(int initialCapacity, float loadFactor, boolean dummy)構造方法區分開來,供LinkedHashSet調用。
略。(是否是有種翻閱課後答案參考書的感受- -)
HashSet無序;容許值爲null;非線程安全;底層增刪等操做基於HashMap實現;
LinkedHashSet有序;容許值爲null;非線程安全;依賴於HashSet,底層增刪等操做基於LinkedHashMap實現;
TreeSet有序;不容許爲null;非線程安全;底層增刪等操做基於TreeMap實現。
從查閱Spliterator相關資料的感覺就是J8的一些技術點在國內應用貌似還不是那麼普及。③中舉了25個java.util.Spliterators在實際項目中的應用,感興趣的同窗能夠深刻學習。
更多有意思的內容,歡迎訪問筆者小站: rebey.cn