用例子解析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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,261评论 0 16
  • 稍不留神黑板成白板
    鲜栗子阅读 255评论 0 4
  • 今天特别想写点啥,是自己想要表达自己吗?也许是吧。 山中岁月无甲子。不觉间已是小半年了,2018年对于自己而言,可...
    大行11阅读 265评论 2 1
  • 凌晨两点钟,一觉醒来,竟无眠。 没有一丝困意,睡前还有点头痛的,可是只睡了三个小时,竟然精神这样好。此刻正是睡眠的...
    风之吻Sam阅读 343评论 0 0
  • 河水漪漪 堤柳如风 我心犹长 街灯昏昏 闻风相坐 我心何徨 树烟缈缈 草木在旁 众人皆忙 天地悠悠 载酒而觞 我心迷乱
    梅山不二阅读 214评论 1 2