當前位置:編程學習大全網 - 編程語言 - Java8裏面的java.util.Spliterator接口有什麽用

Java8裏面的java.util.Spliterator接口有什麽用

首先先直接給壹個答案:Spliterator(splitable iterator可分割叠代器)接口是Java為了並行遍歷數據源中的元素而設計的叠代器,這個可以類比最早Java提供的順序遍歷叠代器Iterator,但壹個是順序遍歷,壹個是並行遍歷

從最早Java提供順序遍歷叠代器Iterator時,那個時候還是單核時代,但現在多核時代下,順序遍歷已經不能滿足需求了...如何把多個任務分配到不同核上並行執行,才是能最大發揮多核的能力,所以Spliterator應運而生啦

因為對於數據源而言...集合是描述它最多的情況,所以Java已經默認在集合框架中為所有的數據結構提供了壹個默認的Spliterator實現,相應的這個實現其實就是底層Stream如何並行遍歷(Stream.isParallel())的實現啦,因此平常用到Spliterator的情況是不多的...因為Java8這次正是壹次引用函數式編程的思想,妳只需要告訴JDK妳要做什麽並行任務,關註業務本身,至於如何並行,怎麽並行效率最高,就交給JDK自己去思考和優化速度了(想想以前寫如何並發的代碼被支配的恐懼吧)作為調用者我們只需要去關心壹些filter,map,collect等業務操作即可

所以想要看Spliterator的實現,可以直接去看JDK對於集合框架的實現,很多實現類妳可以在Spliterators中找到的,也可以直接去妳對應集合的stream方法中找到,比如ArrayList點進去的是Collection的默認實現,只需要提供壹個Spliterator的實現,然後用StreamSupport就可以構造壹個Stream了,相當方便

default Stream<E> stream() {

return StreamSupport.stream(spliterator(), false);

}

@Override

default Spliterator<E> spliterator() {

return Spliterators.spliterator(this, 0);

}

對於Spliterator接口的設計思想,應該要提到的是Java7的Fork/Join(分支/合並)框架,總得來說就是用遞歸的方式把並行的任務拆分成更小的子任務,然後把每個子任務的結果合並起來生成整體結果。帶著這個理解來看看Spliterator接口提供的方法

boolean tryAdvance(Consumer<? super T> action);

Spliterator<T> trySplit();

long estimateSize();

int characteristics();

第壹個方法tryAdvance就是順序處理每個元素,類似Iterator,如果還有元素要處理,則返回true,否則返回false

第二個方法trySplit,這就是為Spliterator專門設計的方法,區分與普通的Iterator,該方法會把當前元素劃分壹部分出去創建壹個新的Spliterator作為返回,兩個Spliterator變會並行執行,如果元素個數小到無法劃分則返回null

第三個方法estimateSize,該方法用於估算還剩下多少個元素需要遍歷

第四個方法characteristics,其實就是表示該Spliterator有哪些特性,用於可以更好控制和優化Spliterator的使用,具體屬性妳可以隨便百度到,這裏就不再贅言

理解了接口方法的意思,現在再來看看自己的實現吧,由於大多數集合都被官方實現了,所以不能搞集合了,只有搞搞類似集合但又不是集合的東西...舉以下這個例子,例子反正感覺不是很好,只能說幫助理解哈Spliterator接口了:

問題:求這個字符串"12%3 21sdas s34d dfsdz45 R3 jo34 sjkf8 3$1P 213ikflsd fdg55 kfd"中所有的數字之和例子:比如這種"12%sdf3",和就是12+3=15,這種"12%3 21sdas"和就是12+3+21=36

思路:字符串要用到Stream,只有把整個字符串拆分成壹個個Character,而是否是並行,按道理講只需要改壹個標誌位即可

先順序執行代碼方式:

/**

* 字符串中的數字計算器實現

*/

public class NumCounter {

private int num;

private int sum;

// 是否當前是個完整的數字

private boolean isWholeNum;

public NumCounter(int num, int sum, boolean isWholeNum) {

this.num = num;

this.sum = sum;

this.isWholeNum = isWholeNum;

}

public NumCounter accumulate(Character c){

if (Character.isDigit(c)){

return isWholeNum ? new NumCounter(Integer.parseInt("" + c), sum + num, false) : new NumCounter(Integer.parseInt("" + num + c), sum, false);

}else {

return new NumCounter(0, sum + num, true);

}

}

public NumCounter combine(NumCounter numCounter){

return new NumCounter(numCounter.num, this.getSum() + numCounter.getSum(), numCounter.isWholeNum);

}

public int getSum() {

return sum + num;

}

}

/**

* 測試類

*/

public class NumCounterTest {

public static void main(String[] args) {

String arr = "12%3 21sdas s34d dfsdz45 R3 jo34 sjkf8 3$1P 213ikflsd fdg55 kfd";

Stream<Character> stream = IntStream.range(0, arr.length()).mapToObj(arr::charAt);

System.out.println("ordered total: " + countNum(stream));

}

private static int countNum(Stream<Character> stream){

NumCounter numCounter = stream.reduce(new NumCounter(0, 0, false), NumCounter::accumulate, NumCounter::combine);

return numCounter.getSum();

}

}

執行結果如下:

如果按照普通方式直接把stream改為並行流...執行結果明顯有點...不對

/**

* 測試類

*/

public class NumCounterTest {

public static void main(String[] args) {

String arr = "12%3 21sdas s34d dfsdz45 R3 jo34 sjkf8 3$1P 213ikflsd fdg55 kfd";

Stream<Character> stream = IntStream.range(0, arr.length()).mapToObj(arr::charAt);

// 調用parallel()變成並行流

System.out.println("ordered total: " + countNum(stream.parallel()));

}

private static int countNum(Stream<Character> stream){

NumCounter numCounter = stream.reduce(new NumCounter(0, 0, false), NumCounter::accumulate, NumCounter::combine);

return numCounter.getSum();

}

}

此時錯誤的並行執行結果如下:

為什麽會執行錯誤,是因為默認的Spliterator在並行時並不知道整個字符串從哪裏開始切割,由於切割錯誤,導致把本來完整的數字比如123,可能就切成了12和3,這樣加起來的數字肯定不對

若是理解了上訴順序執行的NumCounter的邏輯,再來看看Spliterator的實現

/**

* 字符串中的數字分割叠代計算器實現

*/

public class NumCounterSpliterator implements Spliterator<Character> {

private String str;

private int currentChar = 0;

public NumCounterSpliterator(String str) {

this.str = str;

}

@Override

public boolean tryAdvance(Consumer<? super Character> action) {

action.accept(str.charAt(currentChar++));

return currentChar < str.length();

}

@Override

public Spliterator<Character> trySplit() {

int currentSize = str.length() - currentChar;

if (currentSize < 10) return null;

for (int pos = currentSize/2 + currentSize; pos < str.length(); pos++){

if (pos+1 < str.length()){

// 當前Character是數字,且下壹個Character不是數字,才需要劃分壹個新的Spliterator

if (Character.isDigit(str.charAt(pos)) && !Character.isDigit(str.charAt(pos+1))){

Spliterator<Character> spliterator = new NumCounterSpliterator(str.substring(currentChar, pos));

currentChar = pos;

return spliterator;

}

}else {

if (Character.isDigit(str.charAt(pos))){

Spliterator<Character> spliterator = new NumCounterSpliterator(str.substring(currentChar, pos));

currentChar = pos;

return spliterator;

}

}

}

return null;

}

@Override

public long estimateSize() {

return str.length() - currentChar;

}

@Override

public int characteristics() {

return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE;

}

}

/**

* 測試類

*/

public class NumCounterTest {

public static void main(String[] args) {

String arr = "12%3 21sdas s34d dfsdz45 R3 jo34 sjkf8 3$1P 213ikflsd fdg55 kfd";

Stream<Character> stream = IntStream.range(0, arr.length()).mapToObj(arr::charAt);

System.out.println("ordered total: " + countNum(stream));

Spliterator<Character> spliterator = new NumCounterSpliterator(arr);

// 傳入true表示是並行流

Stream<Character> parallelStream = StreamSupport.stream(spliterator, true);

System.out.println("parallel total: " + countNum(parallelStream));

}

private static int countNum(Stream<Character> stream){

NumCounter numCounter = stream.reduce(new NumCounter(0, 0, false), NumCounter::accumulate, NumCounter::combine);

return numCounter.getSum();

}

}

這下可以看到執行結果是正確的了

  • 上一篇:簡歷裏的自我總結
  • 下一篇:顫振編程的詳細說明
  • copyright 2024編程學習大全網