tags : java-collectionsjava
spliterator
是java1.8引入的一種並行遍歷的機制,Iterator
提供也提供了對集合數據進行遍歷的能力,但一個是順序遍歷,一個是並行遍歷。 數組
Arrays
的分割迭代函數有2種形式,
spliterator(xxx [])
,
spliterator(xxx [] , int, int)
, 具體的
xxx
包括
int
,
long
,
double
,
T
,下文就以
int
類型做爲demo進行分析。
Spliterator.OfInt sInt = Arrays.spliterator(arr);
IntConsumer consumer = new IntConsumer() {
@Override
public void accept(int value) {
// TODO Auto-generated method stub
System.out.println(value);
}
};
sInt.tryAdvance(consumer);
複製代碼
程序輸出:1微信
將代碼修改一下:ide
int [] arr = {1,2,3,4,5,6,7,8,9};
Spliterator.OfInt sInt = Arrays.spliterator(arr, 2, 5);
IntConsumer consumer = new IntConsumer() {
@Override
public void accept(int value) {
// TODO Auto-generated method stub
System.out.println(value);
}
};
sInt.tryAdvance(consumer);
複製代碼
程序輸出:3 忽然有點感受,spliterator
的第二個和第三個參數多是指原數組的起始和結束下標,再把代碼修改一下:函數
int [] arr = {1,2,3,4,5,6,7,8,9};
Spliterator.OfInt sInt = Arrays.spliterator(arr, 2, 5);
IntConsumer consumer = new IntConsumer() {
@Override
public void accept(int value) {
// TODO Auto-generated method stub
System.out.println(value);
}
};
sInt.tryAdvance(consumer);
sInt.tryAdvance(consumer);
sInt.tryAdvance(consumer);
sInt.tryAdvance(consumer);
sInt.tryAdvance(consumer);
複製代碼
程序輸出:3 4 5 能夠肯定,spliterator
在迭代時,第二個參數指向數組的起始下標(包括),第三個參數指向原數組的結束下標(不包括)。仍是進入代碼好好研究一下。oop
如下是Arrays
的spliterator
函數:測試
public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) {
return Spliterators.spliterator(array, startInclusive, endExclusive,
Spliterator.ORDERED | Spliterator.IMMUTABLE);
}
複製代碼
繼續跟蹤下Spliterators.spliterator
ui
public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex, int additionalCharacteristics) {
checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex);
return new IntArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics);
}
複製代碼
已經到底層實現了,因爲具體實現時代碼量並不大,故能夠直接看下IntArraySpliterator
類的源碼了this
static final class IntArraySpliterator implements Spliterator.OfInt {
// 指向原數組的array
private final int[] array;
// 分組迭代的起始下標(包括)
private int index;
// 分組迭代的結束下標(不包括)
private final int fence; // one past last index
// 分組迭代的特徵
private final int characteristics;
public IntArraySpliterator(int[] array, int additionalCharacteristics) {
this(array, 0, array.length, additionalCharacteristics);
}
public IntArraySpliterator(int[] array, int origin, int fence, int additionalCharacteristics) {
this.array = array;
this.index = origin;
this.fence = fence;
this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED;
}
@Override
public OfInt trySplit() {
int lo = index, mid = (lo + fence) >>> 1;
return (lo >= mid)
? null
: new IntArraySpliterator(array, lo, index = mid, characteristics);
}
@Override
public void forEachRemaining(IntConsumer action) {
int[] a; int i, hi; // hoist accesses and checks from loop
if (action == null)
throw new NullPointerException();
if ((a = array).length >= (hi = fence) &&
(i = index) >= 0 && i < (index = hi)) {
do { action.accept(a[i]); } while (++i < hi);
}
}
@Override
public boolean tryAdvance(IntConsumer action) {
if (action == null)
throw new NullPointerException();
if (index >= 0 && index < fence) {
action.accept(array[index++]);
return true;
}
return false;
}
@Override
public long estimateSize() { return (long)(fence - index); }
@Override
public int characteristics() {
return characteristics;
}
@Override
public Comparator<? super Integer> getComparator() {
if (hasCharacteristics(Spliterator.SORTED))
return null;
throw new IllegalStateException();
}
}
複製代碼
trySplit
函數很好理解,將原始IntArraySpliterator
的起始下標index和結束下標fence的和的1/2做爲新的分割迭代spliterator
的結束下標,構造新的分割迭代對象,原始對象的index也修改成起始下標index和結束下標fence的和的1/2。spa
tryAdvance
函數對index下標指向的元素執行accept
操做而且index自增1,這就能夠解釋上面例子的結果了。
forEachReaming
函數從index下標開始,對fence
以前的每個元素執行accept
操做。
estimateSize
函數獲取剩餘尚未進行accept
操做的元素的數量。
public interface IntConsumer {
void accept(int value);
default IntConsumer andThen(IntConsumer after) {
Objects.requireNonNull(after);
return (int t) -> { accept(t); after.accept(t); };
}
}
複製代碼
在前面介紹分割迭代時已經講到accept
操做,這個操做是IntConsumer
接口定義的函數。IntConsumer
定義兩個函數,accept
, andThen
。accept
函數接收數組的元素,並對元素進行操做,可是操做自己不改變原數組元素的值;andThen
接口容許提供一個新的IntConsumer
對象,原對象執行完accept
函數後會執行新的對象的accept
函數,看下demo
int [] arr = {1,2,3,4,5,6,7,8,9};
Spliterator.OfInt sInt = Arrays.spliterator(arr, 2, 5);
IntConsumer consumer = new IntConsumer() {
@Override
public void accept(int value) {
// TODO Auto-generated method stub
System.out.println(value);
}
};
sInt.tryAdvance(consumer.andThen(new IntConsumer() {
@Override
public void accept(int value) {
// TODO Auto-generated method stub
System.out.println("i am after");
}
}));
複製代碼
程序輸出:3 i am after
既然說spliterator
是並行遍歷機制,接下來給一個並行遍歷的demo,先定義一個SpliteratorThread
:
public class SpliteratorThread<T> extends Thread {
private Spliterator<T> mSpliterator;
public SpliteratorThread(Spliterator<T> spliterator) {
mSpliterator = spliterator;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
if (mSpliterator != null) {
mSpliterator.forEachRemaining(new Consumer<T>() {
@Override
public void accept(T t) {
// TODO Auto-generated method stub
System.out.println( Thread.currentThread().getName() + "-" + t + " ");
}
});
}
}
}
複製代碼
而後是一個測試入口:
public class Client {
public static void main(String [] args) {
int [] arr = {1,2,3,4,5,6,7,8,9,10};
Spliterator<Integer> spliterator0 = Arrays.spliterator(arr);
Spliterator<Integer> spliterator1 = spliterator0.trySplit();
Spliterator<Integer> spliterator2 = spliterator1.trySplit();
Thread t0 = new SpliteratorThread<>(spliterator0);
t0.setName("t0");
Thread t1 = new SpliteratorThread<>(spliterator1);
t1.setName("t1");
Thread t2 = new SpliteratorThread<>(spliterator2);
t2.setName("t2");
t0.start();
t1.start();
t2.start();
}
}
複製代碼
執行一下: t1-3 t2-1 t0-6 t2-2 t1-4 t0-7 t1-5 t0-8 t0-9 t0-10
對結果進行梳理一下,線程t0遍歷的元素:6,7,8,9,10;線程t1遍歷的元素:3,4,5;線程t2遍歷的元素:1,2。每一個線程內部元素的遍歷是有序的,可是線程的調度是無序的,以上結果顯示了3個線程並行遍歷的流程,這就是分割遍歷最經常使用的場景。
tryAdvance
以後待遍歷的位置後移一位