本文轉載自 jdk8 Stream 解析2 - Spliterator分割迭代器。
概述
我們最為常見的流的產生方式是 collection.stream()
, 你點開Stream()
方法, 他是通過 Collection
這個上層接口兩個java8 新增特性 default method
進行實現。
@Override default Spliterator<E> spliterator() { return Spliterators.spliterator(this, 0); } default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
這就牽扯到一個關鍵要點 Spliterator
:分割迭代器。
本文主要描述 Spliterator
的作用,大部分來源於你不願意讀的 JavaDoc
。
Spliterator
是 jdk8
非常重要的概念。里面的細節很值得學習玩味。
Spliterator 冗長的JavaDoc 說了什么?
基本介紹
Spliterator
是一個對源(數組,集合,io流等等)中元素進行遍歷和分區的類。
可以通過 tryAdvance()
方法逐個遍歷,也可以按照 forEachRemaining()
方法進行按 bulk 逐塊的遍歷。(內部調用的還是tryAdvance)
Spliterator
有類似 Collector
中的 characteristics
, 但都是由十六進制來表示的。
- SIZED :表示大小固定, Collection常用
- DISTINCT : 去重, Set常用
- SORTED : 有順序的 SortedSet 會用
- 等等
原生類型的特化版本
原始類型特化的分割迭代器也被提供,和Stream類似。減少裝箱和拆箱的操作
比迭代器Iterator 更加高效的遍歷元素的方式
提供更加高效的方法,進行數據的迭代。Iterator
的使用需要調用兩個組合方法 hasNext()
以及 next()
,同時在多線程訪問的情況下還會出現競爭,你需要去同步。
而分割迭代器 Spliterator
使用函數式編程的方式,只用一個方法就可以做到這個兩個函數動作。就避免了競爭 ,就是 tryAdvance()
方法。后面會介紹
Spliterator的接口方法
tryAdvance()
同時做了 hasNext()
以及 next()
的工作。
/** * 類似於普通的 Iterator ,它會按順序一個一個使用 Spliterator 中的元素執行action,並且如果還有其他元素要遍歷就返回 true,否則返回 false。 */ boolean tryAdvance(Consumer<? super T> action);
forEachRemaining()
是一個默認方法
,對余下的元素進行操作,直到元素全部被遍歷完
一般情況下會直接調用上面的tryAdvance()
方法,但是也可以根據需要進行重寫。
/** * 對余下的元素進行操作,直到元素全部被遍歷完 * 如果源是有序的,遍歷也是有序的 */ default void forEachRemaining(Consumer<? super T> action) { do { } while (tryAdvance(action)); }
這里有一點很值得注意,方法體中的 do {} 是空的,這個是因為 tryAdvance() 方法本身就完成了兩個操作 hasNext() 以及 next(),所以方法體中不需要有任何操作了。這個是 函數式編程帶來的好處。以及與命令式編程的區別。
trySplit()
嘗試切分源來的 Spliterator
, 返回的是(注意!!!)返回的是 分割出來的那一部分
數據,原有的數據集將不再包含這部分數據集合。兩者 沒有交集
。剩下的可以繼續分割,也許不可以繼續分割了。
舉個例子,我原來有 100個元素,我通過
trySplit
切分出30
個,作為一個新的 Spliterator分割迭代器
返回,原有的,就還剩下70
個。
- 如果是原有數據集合是
ORDERD
的,分出來的也是有序的。 - 除非元素數量是無窮的,否則,最后一定會出現不能再分割的情況,這種情況下,返回的結果是
null。
Spliterator<T> trySplit();
estimateSize()
估算還剩下多少個元素需要遍歷,不一定精確。
但是如果這個 Spliterator
是 SIZED
,沒有被遍歷或者 split, 或是 SUBSIZED
的,沒有被遍歷,那么他這個值一定是准確的。
long estimateSize();
還有個與之相關的默認方法,就是利用這個特性。
default long getExactSizeIfKnown() { return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); }
characteristics()
- 分割之前,返回的結果都是一致的
- 而分割之后,不保證一致
有一個默認方法用於判斷 Spliterator
是否包含這個特性
default boolean hasCharacteristics(int characteristics) { return (characteristics() & characteristics) == characteristics; }
getComparator
如果源是SORTED
類型的,且有比較器 Comparator
的話,則返回這個 Comparator
,如果是SORTED
類型的,但是沒有比較器,則返回 null
, 除此之外,都拋出異常。
接口的默認方法里,就是拋出了異常。
default Comparator<? super T> getComparator() { throw new IllegalStateException(); }
Spliterator的8個Characteristics 特性
ORDERED
源的元素有序,tryAdvance
,forEachRemaining
和 trySplit
都會保證有序的進行元素的處理。
- 需要注意
hashSet
這類Collection
是不保證有序的 - 有
ORDERED
特性的數據,在並發計算的時候客戶端也要做順序限制的保證
DISTINCT
太簡單,唯一性。 類似 Set
這樣的傳入集合會擁有這樣的特性
SORTED
有這種特性的 Spliterator
,有一個特定的順序。或者是所有元素都是可比較的,或者是有特定的比較器。
有
SORTED
一定會有ORDERED
SIZED
有這種屬性的 Spliterator
在遍歷和分割之前,estimateSize()
返回的大小是固定的,並且是准確的。
NONNULL
不為 NULL
, 大部分並發的集合,隊列,Map 都可能會有這樣的特性。
IMMUTABLE
不可變的。元素遍歷期間不可以被 添加,替換,刪除(cannot be added, replaced, or removed),否則,應該拋出異常。
CONCURRENT
支持並發操作的。
- 頂層的
Spliterator
不可以CONCURRENT
與SIZED
。 這兩者是相互沖突的。 - 但是分割之后的
Spliterator
, 可能是SIZED
, 頂層不能決定底層
SUBSIZED
該 Spliterator 和所有從它拆分出來的分割迭代器都是 SIZED
以及 SUBSIZED
的。
如果分割后,沒有按照要求返回SIZED
以及 SUBSIZED
屬性,那么操作是不被保證的,也就是結果不可預測。
這個屬性和
SIZED
的區別就是,SIZED
不保證SUBSIZED
。而SUBSIZED
會要求保證SIZED
內部特化而做的函數式接口 (OfPrimitive)
除了上面的函數,以及特性,Spliterator
迭代器中,還有幾個定義在內部的接口。
OfPrimitive
重載了(overloads)了 Spliterator
的方法。用於實現特化的分割迭代器。
overloads:參數列表不同,函數名相同,與返回值類型無關,與訪問修飾符無關。
注意與
override
的區別
免責聲明:
本文轉自網絡文章,轉載此文章僅為個人收藏,分享知識,如有侵權,請聯系博主進行刪除。
原文作者:待葡萄嗖透
原文出處:http://movingon.cn/2017/05/02/jdk8-Stream-%E8%A7%A3%E6%9E%902-Spliterator%E5%88%86%E5%89%B2%E8%BF%AD%E4%BB%A3%E5%99%A8/