基于JDK1.8的Spliterator划分遍历源码阅读,讲解ArrayList、LinkedList和HashMap中划分遍历(三)

本文详细解析了LinkedList中的LLSpliterator类,探讨了其如何实现链表元素的遍历与划分。从源码角度介绍了LLSpliterator的属性、构造方法及核心方法,如characteristics、estimateSize、tryAdvance等,并分享了均衡划分的使用技巧。

一、源码阅读-知晓这个类到底是什么?

1、当调用LinkedList中的方法spliterator()

此方法返回的是一个内部类对象,用以遍历链表中的元素。
    @Override
    public Spliterator<E> spliterator() {
        return new LLSpliterator<E>(this, -1, 0);
    }

2、源码结构分析

1) 属性

		//最小遍历单元,就是指当前Spliterator划分遍历对象中至少应当遍历的大小
		static final int BATCH_UNIT = 1 << 10;  // batch array size increment
		//最大遍历单元,就是指当前Spliterator划分遍历对象中最多只能遍历的大小
        static final int MAX_BATCH = 1 << 25;  // max batch array size;
        //当前集合
        final LinkedList<E> list; // null OK unless traversed
        //由于链表中没有显式使用下标索引,因此使用current代表当前操作指针
        Node<E> current;      // current node; null until initialized
        //est预估大小,
        int est;              // size estimate; -1 until first needed
        //如果读过LinkedList源码的话,就应该知道这个值等于值集合的modCount,
    	//modCount是继承自AbstractList的属性
    	//用来防止并发导致集合发生改变,一旦改变就会抛出异常。
        int expectedModCount; // initialized when est set
        //已遍历的大小
        int batch;            // batch size for splits

2) 构造方法

	/** 
		初始化,list集合,初始化预估大小为-1,预期大小为0
 	*/
       LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
            this.list = list;
            this.est = est;
            this.expectedModCount = expectedModCount;
        }

3) 核心方法

  • 方法1:
    //LinkedList中的数据特征值就是基于链表,那么具有ORDERED,
    //list一定会指定大小size(),那么就具有SIZED 
    //ArrayList可以调用trySplit()时,也可以直接确认子Spliterator的大小。
    public int characteristics() {
            return 
            Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
        }
    
  • 方法2:
    //getFence()强制初始化fence的值,获得预估大小
    public long estimateSize() {
            public long estimateSize() { return (long) getEst(); }
        }
    
  • 方法3:
    final int getEst() {
            int s; // force initialization
            final LinkedList<E> lst;
             //如果est小于0,表示初始化
            if ((s = est) < 0) {
            	//如果集合为null,那么est也是0
                if ((lst = list) == null)
                    s = est = 0;
                else {
                //获得list集合的modCount
                    expectedModCount = lst.modCount;
                    //current节点直接等于LinkedList的属性first节点开始遍历
                    current = lst.first;
                    s = est = lst.size;
                }
            }
            return s;
        }
    
  • 方法4:
    //Consumer就是一个消费者
    public boolean tryAdvance(Consumer<? super E> action) {
            Node<E> p;
            if (action == null) throw new NullPointerException();
            //初始化est,每消费一次,est预估大小直接--  
            //p = current) != null表示为null就不需要消费了
            if (getEst() > 0 && (p = current) != null) {
                --est;
                //消费的是Node节点的Object属性对象
                E e = p.item;
                //消费完毕后,current节点直接等于next节点
                current = p.next;
                action.accept(e);
                if (list.modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                return true;
            }
            return false;
        }
    
  • 方法5:
    //Consumer就是一个消费者
    public void forEachRemaining(Consumer<? super E> action) {
            Node<E> p; int n;
            if (action == null) throw new NullPointerException();
            //初始化est 
            //p = current) != null表示为null就不需要消费了
            if ((n = getEst()) > 0 && (p = current) != null) {
                //消费完毕,current直接重置
                current = null;
                //消费完毕,est剩余大小直接为0
                est = 0;
                do {
                    E e = p.item;
                    p = p.next;
                    action.accept(e);
                } while (p != null && --n > 0);
            }
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    
  • 方法6:
    //子划分遍历
    public Spliterator<E> trySplit() {
            Node<E> p;
            //初始化est 
            int s = getEst();
            //
            if (s > 1 && (p = current) != null) {
            	//第一次遍历batch=0,BATCH_UNIT=1024,之后再划分当前Spliterator的话,batch=上一次子划分出去的遍历大小
                int n = batch + BATCH_UNIT;
                //如果n超出了集合大小,则取集合大小
                if (n > s)
                    n = s;
                //超出上限,则取上限 1<<30
                if (n > MAX_BATCH)
                    n = MAX_BATCH;
                //这里的重点是将链表转为数组来遍历。
                Object[] a = new Object[n];
                int j = 0;
                do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
                //取完子遍历的数据,当前节点就等于子遍历最后一个节点的下一个节点
                current = p;
                //batch等于子遍历大小
                batch = j;
                //剩余预估大小等于当前this的总数量-子遍历划分出去的数量
                est = s - j;
                return 
                //返回由Spliterators工具中的静态方法,这里数据特征是ORDERED有序性
                //本质是一个Spliterators.ArraySpliterator对象,其内部维护的是Object[] array;
                Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
            }
            return null;
        }
    

在这里插入图片描述

4) 使用技巧

均衡划分,每个子Spliterator均划分同样的大小。
A,A->B, A->C ,C->D。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值