用例子解析Spliterator

完整代码:代码

前言

Iterator不同的是,Spliterator可以拆分成多份去遍历,有点像二分法,每次把某个Spliterator平均分成两份,但是改的只是下标.

话不多说,先通过一个例子来说明吧

小例子

这个是我从ArrayList的源码里面拿出来的一个ArrayListSpliterator,只是去掉了modCount变量.

需要实现Spliterator接口, 接口定义大家可以自己简单去查看一下,就是要实现以下的一些方法.

    static final class ArrayListSpliterator implements Spliterator {
        
        //用于存放实体变量的list
        private final ArrayList list;
        //遍历的当前位置
        private int index; 
        //结束位置(不包括) 意思是当前可用的元素是[index, fence) = [index, fence-1] 
        private int fence; // -1 until used; then one past last index

        // 构造方法
        ArrayListSpliterator(ArrayList 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 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 trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid) ? null : // divide range in half unless too small
                new ArrayListSpliterator(list, lo, index = mid);
        }
        
        //单次遍历  下标index只加1
        public boolean tryAdvance(Consumer 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 action) {
            int i, hi, mc; // hoist accesses and checks from loop
            ArrayList 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 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 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(){
            @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 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(){
            @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

你可能感兴趣的:(用例子解析Spliterator)