用例子解析Spliterator

完整代碼:代碼

前言

Iterator不同的是,Spliterator可以拆分成多份去遍歷,有點像二分法,每次把某個Spliterator平均分成兩份,但是改的只是下標.

話不多說,先通過一個例子來說明吧

小例子

這個是我從ArrayList的源碼里面拿出來的一個ArrayListSpliterator,只是去掉了modCount變量.

需要實現Spliterator<E>接口, 接口定義大家可以自己簡單去查看一下,就是要實現以下的一些方法.

    static final class ArrayListSpliterator<E> implements Spliterator<E> {
        
        //用于存放實體變量的list
        private final ArrayList<E> list;
        //遍歷的當前位置
        private int index; 
        //結束位置(不包括) 意思是當前可用的元素是[index, fence) = [index, fence-1] 
        private int fence; // -1 until used; then one past last index

        // 構造方法
        ArrayListSpliterator(ArrayList<E> list, int origin, int fence) {
            this.list = list; 
            this.index = origin;
            this.fence = fence;
        }
        
        //第一次使用的時候初始化fence 返回結束位置
        private int getFence() { // initialize fence to size on first use
            int hi; // (a specialized variant appears in method forEach)
            ArrayList<E> lst;
            if ((hi = fence) < 0) {
                if ((lst = list) == null)
                    hi = fence = 0;
                else {
                    hi = fence = lst.size();
                }
            }
            return hi;
        }
        
        /**
         * 根據當前的Spliterator拆分出一個新的Spliterator
         * 相當于二分,
         * Note:共享同一個list,改變的只是下標
         */
        public ArrayListSpliterator<E> trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid) ? null : // divide range in half unless too small
                new ArrayListSpliterator<E>(list, lo, index = mid);
        }
        
        //單次遍歷  下標index只加1
        public boolean tryAdvance(Consumer<? super E> action) {
            if (action == null)
                throw new NullPointerException();
            int hi = getFence(), i = index;
            if (i < hi) {
                index = i + 1;
                @SuppressWarnings("unchecked") E e = (E)list.get(i);
                action.accept(e);
                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.toArray()) != null) {
                if ((hi = fence) < 0) {
                    hi = lst.size();
                }
                if ((i = index) >= 0 && (index = hi) <= a.length) {
                    for (; i < hi; ++i) {
                        @SuppressWarnings("unchecked") E e = (E) a[i];
                        action.accept(e);
                    }
                }
            }
        }
        
        //剩下還有多少元素
        public long estimateSize() {
            return (long) (getFence() - index);
        }
        
        public int characteristics() {
            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
        }
        
        public String toString() {
                return "[" + this.index + "," + getFence() + "]";
        }
    }

測試1: 測試trySplit()方法,觀察Spliterator是如何進行拆分的.

public class TestSpliterator {
    public static void main(String[] args) {
        test_trySplit();
    }

    public static void test_trySplit() {
        ArrayList<Integer> al = new ArrayList<>();
        for (int i = 0; i <= 10; i++) al.add(i);
        ArrayListSpliterator als_1 = new ArrayListSpliterator(al, 0, -1);
        System.out.println("als_1:" + als_1);    // [0,11]
        
System.out.println("---------split-----------");
        ArrayListSpliterator als_2 = als_1.trySplit();
        System.out.println("als_1:" + als_1);    // [5,11]
        System.out.println("als_2:" + als_2);    // [0,5]
        
        // [0,11](als_1) ---> [0,5](als_2) + [5,11](als_1)
        
System.out.println("---------split-----------");
        ArrayListSpliterator als_3 = als_1.trySplit();
        ArrayListSpliterator als_4 = als_2.trySplit();
        System.out.println("als_1:" + als_1);
        System.out.println("als_2:" + als_2);
        System.out.println("als_3:" + als_3);
        System.out.println("als_4:" + als_4);
        
        /**
         * [0,5](als_2)  --> [0,2](als_4)  + [2,5](als_2)
         * [5,11](als_1) --> [8,11](als_1) + [5,8](als_3)
         */
        
System.out.println("---------test the address---------");
        System.out.println("(als_1.list == als_2.list) = " + (als_1.list == als_2.list));
        System.out.println("(als_2.list == als_3.list) = " + (als_2.list == als_3.list));
        System.out.println("(als_3.list == als_4.list) = " + (als_3.list == als_4.list));
    }
}

輸出1: 對照源碼和測試代碼結果就可以看出

1.所有Spliterator都共享一個list,因為擁有的是同一個list的地址.
2.是按下標進行二分拆分.

als_1:[0,11]
---------split-----------
als_1:[5,11]
als_2:[0,5]
---------split-----------
als_1:[8,11]
als_2:[2,5]
als_3:[5,8]
als_4:[0,2]
---------test the address---------
(als_1.list == als_2.list) = true
(als_2.list == als_3.list) = true
(als_3.list == als_4.list) = true

測試2:測試forEachRemaining方法,觀察indexestimateSize()的變化

public class TestSpliterator {
    public static void main(String[] args) {
        test_forEachRemaining();
        System.out.println("---------------------");
        test_tryAdvance();
    }
    
    public static void test_tryAdvance() {
        ArrayList<Integer> al = new ArrayList<>();
        for (int i = 0; i <= 10; i++) al.add(i);
        ArrayListSpliterator als_1 = new ArrayListSpliterator(al, 0, -1);
        als_1.tryAdvance(new Consumer<Integer>(){
            @Override
            public void accept(Integer t) {
                System.out.print(t + " ");
            }
        });
        System.out.println("\nals_1:" + als_1);
        System.out.println("left size:" + als_1.estimateSize());
    }
    
    public static void test_forEachRemaining() {
        ArrayList<Integer> al = new ArrayList<>();
        for (int i = 0; i <= 10; i++) al.add(i);
        ArrayListSpliterator als_1 = new ArrayListSpliterator(al, 0, -1);
        als_1.forEachRemaining(new Consumer<Integer>(){
            @Override
            public void accept(Integer t) {
                System.out.print(t + " ");
            }
        });
        System.out.println("\nals_1:" + als_1);
        System.out.println("left size:" + als_1.estimateSize());
    }
}

輸出2:

1.forEachRemainingindex已經和getFence()相等了,并且剩下的size已經沒有了,表示已經消費完了.
2.tryAdvance中只是消費了一個,所以index只是增加了1,并且剩下的size只是減少了1.

0 1 2 3 4 5 6 7 8 9 10 
als_1:[11,11]
left size:0
---------------------
0 
als_1:[1,11]
left size:10

int characteristics()方法

a representation of characteristics這個是定義了該Spliterator的屬性. 建議大家自己去看一下英文的注釋解釋得比較清楚.

  public static final int CONCURRENT = 0x00001000;  //表示線程安全的
  public static final int DISTINCT   = 0x00000001;        // 元素是獨一無二的
  public static final int IMMUTABLE  = 0x00000400;    //元素不可變  在遍歷過程中不能刪除修改增加
  public static final int NONNULL    = 0x00000100;     //元素不能為null
  public static final int ORDERED    = 0x00000010;    //迭代器按照原始的順序迭代
  public static final int SIZED      = 0x00000040;        //元素可以計數
  public static final int SORTED     = 0x00000004;     //元素是有序的
  public static final int SUBSIZED = 0x00004000;

參考

1.java1.8 java.util.ArrayList

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、基本數據類型 注釋 單行注釋:// 區域注釋:/* */ 文檔注釋:/** */ 數值 對于byte類型而言...
    龍貓小爺閱讀 4,288評論 0 16
  • 稍不留神黑板成白板
    鮮栗子閱讀 258評論 0 4
  • 今天特別想寫點啥,是自己想要表達自己嗎?也許是吧。 山中歲月無甲子。不覺間已是小半年了,2018年對于自己而言,可...
    大行11閱讀 272評論 2 1
  • 凌晨兩點鐘,一覺醒來,竟無眠。 沒有一絲困意,睡前還有點頭痛的,可是只睡了三個小時,竟然精神這樣好。此刻正是睡眠的...
    風之吻Sam閱讀 350評論 0 0
  • 河水漪漪 堤柳如風 我心猶長 街燈昏昏 聞風相坐 我心何徨 樹煙緲緲 草木在旁 眾人皆忙 天地悠悠 載酒而觴 我心迷亂
    梅山不二閱讀 219評論 1 2