Java8系列 (三) Spliterator可分迭代器


本文轉載自 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM