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