1. 程式人生 > >JDK8原始碼之Spliterator並行遍歷迭代器

JDK8原始碼之Spliterator並行遍歷迭代器

Spliterator是什麼?

public interface Spliterator<T> {

Spliterator是一個可分割迭代器(splitable iterator),可以和iterator順序遍歷迭代器一起看。jdk1.8釋出後,對於並行處理的能力大大增強,Spliterator就是為了並行遍歷元素而設計的一個迭代器,jdk1.8中的集合框架中的資料結構都預設實現了spliterator,後面我們也會結合ArrayList中的spliterator()一起解析。

Spliterator內部結構

 //單個對元素執行給定的動作,如果有剩下元素未處理返回true,否則返回false
boolean tryAdvance(Consumer<? super T> action); //對每個剩餘元素執行給定的動作,依次處理,直到所有元素已被處理或被異常終止。預設方法呼叫tryAdvance方法 default void forEachRemaining(Consumer<? super T> action) { do { } while (tryAdvance(action)); } //對任務分割,返回一個新的Spliterator迭代器 Spliterator<T> trySplit(); //用於估算還剩下多少個元素需要遍歷
long estimateSize(); //當迭代器擁有SIZED特徵時,返回剩餘元素個數;否則返回-1 default long getExactSizeIfKnown() { return (characteristics() & SIZED) == 0 ? -1L : estimateSize(); } //返回當前物件有哪些特徵值 int characteristics(); //是否具有當前特徵值 default boolean hasCharacteristics(int characteristics) { return (characteristics() & characteristics) == characteristics; } //如果Spliterator的list是通過Comparator排序的,則返回Comparator
//如果Spliterator的list是自然排序的 ,則返回null //其他情況下拋錯 default Comparator<? super T> getComparator() { throw new IllegalStateException(); }

特徵值其實就是為表示該Spliterator有哪些特性,用於可以更好控制和優化Spliterator的使用。關於獲取比較器getComparator這一個方法,目前我還沒看到具體使用的地方,所以可能理解有些誤差。特徵值如下:(部分屬於猜測)

 //
 public static final int ORDERED    = 0x00000010;
 //
 public static final int DISTINCT   = 0x00000001;
 //排序
 public static final int SORTED     = 0x00000004;
 //大小
 public static final int SIZED      = 0x00000040;
 //沒有null
 public static final int NONNULL    = 0x00000100;
 public static final int IMMUTABLE  = 0x00000400;
 public static final int CONCURRENT = 0x00001000;
 public static final int SUBSIZED = 0x00004000;

ArrayListSpliterator

static final class ArrayListSpliterator<E> implements Spliterator<E> {
    //用於存放ArrayList物件
   private final ArrayList<E> list;
   //起始位置(包含),advance/split操作時會修改
   private int index; 
   //結束位置(不包含),-1 表示到最後一個元素
   private int fence; 
   //用於存放list的modCount
   private int expectedModCount; 

   ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
                             int expectedModCount) {
            this.list = list; 
            this.index = origin;
            this.fence = fence;
            this.expectedModCount = expectedModCount;
        }
    //獲取結束位置(存在意義:首次初始化石需對fence和expectedModCount進行賦值)
   private int getFence() { 
        int hi; 
        ArrayList<E> lst;
        //fence<0時(第一次初始化時,fence才會小於0):
        if ((hi = fence) < 0) {
            //list 為 null時,fence=0
            if ((lst = list) == null)
                hi = fence = 0;
            else {
            //否則,fence = list的長度。
                expectedModCount = lst.modCount;
                hi = fence = lst.size;
            }
        }
        return hi;
    }
    //分割list,返回一個新分割出的spliterator例項
    public ArrayListSpliterator<E> trySplit() {
        //hi為當前的結束位置
        //lo 為起始位置
        //計算中間的位置
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        //當lo>=mid,表示不能在分割,返回null
        //當lo<mid時,可分割,切割(lo,mid)出去,同時更新index=mid
        return (lo >= mid) ? null : 
            new ArrayListSpliterator<E>(list, lo, index = mid,                                         expectedModCount);
    }
    //返回true 時,只表示可能還有元素未處理
    //返回false 時,沒有剩餘元素處理了。。。
    public boolean tryAdvance(Consumer<? super E> action) {
         if (action == null)
             throw new NullPointerException();
         //hi為當前的結束位置
         //i 為起始位置
         int hi = getFence(), i = index;
         //還有剩餘元素未處理時
         if (i < hi) {
             //處理i位置,index+1
             index = i + 1;
             @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
             action.accept(e);
             //遍歷時,結構發生變更,拋錯
             if (list.modCount != expectedModCount)
                 throw new ConcurrentModificationException();
             return true;
         }
         return false;
     }
    //順序遍歷處理所有剩下的元素
   public void forEachRemaining(Consumer<? super E> action) {
       int i, hi, mc; // hoist accesses and checks from loop
       ArrayList<E> lst; Object[] a;
       if (action == null)
           throw new NullPointerException();
       if ((lst = list) != null && (a = lst.elementData) != null) {
           //當fence<0時,表示fence和expectedModCount未初始化,可以思考一下這裡能否直接呼叫getFence(),嘿嘿?
           if ((hi = fence) < 0) {
               mc = lst.modCount;
               hi = lst.size;
           }
           else
               mc = expectedModCount;
           if ((i = index) >= 0 && (index = hi) <= a.length) {
               for (; i < hi; ++i) {
                   @SuppressWarnings("unchecked") E e = (E) a[i];
                   //呼叫action.accept處理元素
                   action.accept(e);
               }
               //遍歷時發生結構變更時丟擲異常
               if (lst.modCount == mc)
                   return;
           }
       }
       throw new ConcurrentModificationException();
   }

   public long estimateSize() {
        return (long) (getFence() - index);
    }

    public int characteristics() {
        //打上特徵值:、可以返回size
        return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
    }
}

測試程式碼如下:

 List<String>  arrs = new ArrayList<>();
 arrs.add("a");
 arrs.add("b"); 
 arrs.add("c");
 arrs.add("d");
 arrs.add("e");
 arrs.add("f");
 arrs.add("h");
 arrs.add("i");
 arrs.add("j");
 Spliterator<String> a =  arrs.spliterator();
 //此時結果:a:0-9(index-fence)
 Spliterator<String> b = a.trySplit();
 //此時結果:b:4-9,a:0-4
 Spliterator<String> c = a.trySplit();
 //此時結果:c:4-6,b:4-9,a:6-9
 Spliterator<String> d = a.trySplit();
 //此時結果:d:6-7,c:4-6,b:4-9,a:7-9

可以看到每次分割,都會分割剩餘的前一半,fence之不變,index後移。同時也發現:
1.ArrayListSpliterator本質上還是對原list進行操作,只是通過index和fence來控制每次處理範圍
2.也可以得出,ArrayListSpliterator在遍歷元素時,不能對list進行結構變更操作,否則拋錯。

衍生介面OfPrimitive

可以看到Spliterator類裡面可以看到這麼一個介面,那麼這個介面是幹什麼的,原始碼:

 public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
            extends Spliterator<T> {
        @Override
        T_SPLITR trySplit();
        @SuppressWarnings("overloads")
        boolean tryAdvance(T_CONS action);
        @SuppressWarnings("overloads")
        default void forEachRemaining(T_CONS action) {
            do { } while (tryAdvance(action));
        }
    }

可以看到,這個介面基本沒有變動,這是多增加兩個泛型宣告而已,本質上和Spliterator沒有太大的區別,只不過,它限制tryAdvance的引數action型別T_CONS和trySplit的返回引數T_SPLITR必須在實現介面時先宣告型別。
基於OfPrimitive介面,又衍生出了OfInt、OfLong、OfDouble等專門用來處理int、Long、double等分割迭代器介面(在Spliterators有具體的實現)。

Spliterator.OfInt的實現

在Spliterators類中,已經實現了針對int[]資料分割迭代器,和ArrayList差不多,所以接下來只會簡單的閱讀一下:

 //與ArrayList不同的是,array是實現宣告的,因此不必擔心遍歷過程中發生結構變更。
 static final class IntArraySpliterator implements Spliterator.OfInt {
    private final int[] array;
    private int index;       
     private final int fence;
     //用於記錄特徵值
     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();
     }
    }