Spliterator并行遍歷迭代器(JDK8)

文章前半部分轉(zhuǎn)自:https://blog.csdn.net/lh513828570/article/details/56673804

Spliterator是什么?

public interface Spliterator<T> 

Spliterator是一個可分割迭代器(splitable iterator),可以和iterator順序遍歷迭代器一起看。jdk1.8發(fā)布后,對于并行處理的能力大大增強,Spliterator就是為了并行遍歷元素而設(shè)計的一個迭代器,jdk1.8中的集合框架中的數(shù)據(jù)結(jié)構(gòu)都默認實現(xiàn)了spliterator,后面我們也會結(jié)合ArrayList中的spliterator()一起解析。

 //單個對元素執(zhí)行給定的動作,如果有剩下元素未處理返回true,否則返回false
 boolean tryAdvance(Consumer<? super T> action);

 //對每個剩余元素執(zhí)行給定的動作,依次處理,直到所有元素已被處理或被異常終止。默認方法調(diào)用tryAdvance方法
 default void forEachRemaining(Consumer<? super T> action) {
    do { } while (tryAdvance(action));
 }

 //對任務(wù)分割,返回一個新的Spliterator迭代器
 Spliterator<T> trySplit();

 //用于估算還剩下多少個元素需要遍歷
 long estimateSize();

 //當?shù)鲹碛蠸IZED特征時,返回剩余元素個數(shù);否則返回-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有哪些特性,用于可以更好控制和優(yōu)化Spliterator的使用。關(guān)于獲取比較器getComparator這一個方法,目前我還沒看到具體使用的地方,所以可能理解有些誤差。(源瑪里有這里就不展示了)

ArrayList的例子

ArrayListSpliterator在ArrayList的源碼里

static final class ArrayListSpliterator<E> implements Spliterator<E> {
    //用于存放ArrayList對象
   private final ArrayList<E> list;
   //起始位置(包含),advance/split操作時會修改
   private int index; 
   //結(jié)束位置(不包含),-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;
        }
    //獲取結(jié)束位置(存在意義:首次初始化石需對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為當前的結(jié)束位置
        //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為當前的結(jié)束位置
         //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);
             //遍歷時,結(jié)構(gòu)發(fā)生變更,拋錯
             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未初始化,可以思考一下這里能否直接調(diào)用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];
                   //調(diào)用action.accept處理元素
                   action.accept(e);
               }
               //遍歷時發(fā)生結(jié)構(gòu)變更時拋出異常
               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();
        System.out.println(a);
        //此時結(jié)果:a:0-9(index-fence)

        Spliterator<String> b = a.trySplit();
        System.out.println(b.toString());
        //此時結(jié)果:b:4-9,a:0-4

        Spliterator<String> c = a.trySplit();
        //此時結(jié)果:c:4-6,b:4-9,a:6-9
        System.out.println(c.toString());

        Spliterator<String> d = a.trySplit();
        System.out.println(d.toString());
        //此時結(jié)果:d:6-7,c:4-6,b:4-9,a:7-9

可以看到每次分割,都會分割剩余的前一半,fence之不變,index后移。同時也發(fā)現(xiàn):
1.ArrayListSpliterator本質(zhì)上還是對原list進行操作,只是通過index和fence來控制每次處理范圍
2.也可以得出,ArrayListSpliterator在遍歷元素時,不能對list進行結(jié)構(gòu)變更操作,否則拋錯。

衍生接口OfPrimitive

源碼:

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));
        }
    }
 public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>> extends Spliterator<T> {
        T_SPLITR trySplit();
        boolean tryAdvance(T_CONS var1);
        default void forEachRemaining(T_CONS var1) {
            while(this.tryAdvance(var1)) {
                ;
            }

        }
    }

可以看到,這個接口基本沒有變動,這是多增加兩個泛型聲明而已,本質(zhì)上和Spliterator沒有太大的區(qū)別,只不過,它限制tryAdvance的參數(shù)action類型T_CONS和trySplit的返回參數(shù)T_SPLITR必須在實現(xiàn)接口時先聲明類型。
基于OfPrimitive接口,又衍生出了OfInt、OfLong、OfDouble等專門用來處理int、Long、double等分割迭代器接口(在Spliterators有具體的實現(xiàn))。
LinkedHashSet中的Spliterator方法的實現(xiàn),內(nèi)部使用到了Sqlieterator接口的常量值!

簡單的并發(fā)測試(jdk1.8+)

    private AtomicInteger count = new AtomicInteger(0);
    private List<String> strList = createList();
    private Spliterator spliterator = strList.spliterator();

    /**
     * 多線程計算list中數(shù)值的和
     * 測試spliterator遍歷
     */
    @Test
    public void mytest() {
        for (int i = 0; i < 4; i++) {
            new Thread(() -> {
                String threadName = Thread.currentThread().getName();
                System.out.println("   " + threadName + " start ");
                spliterator.trySplit().forEachRemaining((o) -> {
                    if (isInteger((String) o)) {
                        int num = Integer.parseInt(o + "");
                        count.addAndGet(num);
                        System.out.println("數(shù)值:" + num + "     " + threadName);
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                System.out.println("     " + threadName + " end");
            }).start();
        }
        try {
            Thread.sleep(15000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("結(jié)果為:" + count);
    }

    private List<String> createList() {
        List<String> result = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            if (i % 10 == 0) {
                result.add(i + "");
            } else {
                result.add("=");
            }
        }
        return result;
    }

    public static boolean isInteger(String str) {
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
        return pattern.matcher(str).matches();
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容