jdk8中Spliterator的作用


  之前的時候看集合部分源碼沒看完,今天又翻了一下,看到了個東西spliterator,感覺挺陌生。查了一下,網上解讀源碼的挺多,但沒有使用的例子,於是看了下代碼,准備自己寫個例子試試。
源碼部分,靈小帝的博客已經說的很清楚了,摘抄如下:
--------------------------------------------------------------------------------------------------------------------------
Spliterator是什么
  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這一個方法,目前我還沒看到具體使用的地方,所以可能理解有些誤差。特征值如下:(部分屬於猜測)
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;
    }
}
--------------------------------------------------------------------------------------------------------------------------
  以上為源碼講解部分,大意是說,這個就是用來多線程並行迭代的迭代器,這個迭代器的主要作用就是把集合分成了好幾段,每個線程執行一段,因此是線程安全的。基於這個原理,以及modCount的快速失敗機制,如果迭代過程中集合元素被修改,會拋出異常。
  我們設計一個測試用例:創建一個長度為100的list,如果下標能被10整除,則該位置數值跟下標相同,否則值為aaaa。然后多線程遍歷list,取出list中的數值(字符串aaaa不要)進行累加求和。
測試代碼如下:
public class Atest {
    AtomicInteger count = new AtomicInteger(0);
    List<String> strList = createList();
    Spliterator spliterator = strList.spliterator();

    /**
     * 多線程計算list中數值的和
     * 測試spliterator遍歷
     */
    @Test
    public void mytest(){
        for(int i=0;i<4;i++){
            new MyThread().start();
        }
        try {
            Thread.sleep(15000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("結果為:" + count);
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println("線程"+threadName+"開始運行-----");
            spliterator.trySplit().forEachRemaining(new Consumer() {
                @Override
                public void accept(Object o) {
                    if(isInteger((String)o)){
                        int num = Integer.parseInt(o +"");
                        count.addAndGet(num);
                        System.out.println("數值:"+num+"------"+threadName);
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            System.out.println("線程"+threadName+"運行結束-----");
        }
    }

    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("aaa");
            }
        }
        return result;
    }

    public static boolean isInteger(String str) {
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
        return pattern.matcher(str).matches();
    }
}
  輸出結果:
  可以看到,有4個線程執行了該方法,並得到了正確的結果,並發遍歷執行沒有問題。
 
 
 


免責聲明!

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



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