zoukankan      html  css  js  c++  java
  • Java源码阅读------ArrayList(2)

    迭代器

    在ArrayList中主要包含了两种迭代器分别是Itr与ListItr,在java8之后又加入了ArrayListSpliterator这个迭代器。这些迭代器分别实现了Iterable,ListIterator,Spliterator接口中的方法。

    Itr

    Itr类实现了Iterator接口的方法,主要的变量有cursor,lastRet与expectedModCount。

    int cursor;       // 指向下一个要遍历返回的元素
    int lastRet = -1; // 指向上一次遍历返回的元素。如果没有就置为-1
    int expectedModCount = modCount;//初始化的时候将在遍历前顺序表已经修改的次数记录
    

    fail-fast

    这里简单的讲一下fail-fast机制,上文中我们对顺序表的操作都在维护一个叫modCount的变量,我们每对顺序表修改一次modCount就会对应发生改变,而在Itr初始化的时候,将最近一次modCount的计数给了expectedModCount,并实现了这个方法:

    final void checkForComodification() {
    	if (modCount != expectedModCount)
        	throw new ConcurrentModificationException();
    }
    

    当在遍历的过程中出现了对顺序表的修改情况,modCount 就会发生改变,这时就会抛出ConcurrentModificationException异常,这就是fail-fast机制最直接的一个解释,这个机制保证了在遍历的过程中不会发生对顺序表的修改。

    移除

    但是在进行遍历的时候还是有修改的操作的,Itr类给出了一个移除的操作:

    	public void remove() {
        	if (lastRet < 0)//移除的是之前返回的元素,如果没有就抛出异常
                throw new IllegalStateException();
            checkForComodification();//检查fail-fast
            try {
                ArrayList.this.remove(lastRet);//使用原先的移除方法
                cursor = lastRet;//修改索引
                lastRet = -1;//之前的元素已经被移除
                expectedModCount = modCount;
                //最重要的将expectedModCount刷新,否则无法通过fail-fast检查
            } catch (IndexOutOfBoundsException ex) {//这个异常来自ArrayList的remove方法中对索引的检查
                throw new ConcurrentModificationException();
            }    
        }
    

    遍历

    根据cursor的值来判断是否含有下一个

    public boolean hasNext() {
        return cursor != size;
    }
    

    获取下一个:

    public E next() {
    	checkForComodification();//检查fail-fast
        int i = cursor;
        if (i >= size)//对cursor进行检查
        	throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;//获取数组元素
        if (i >= elementData.length)//判断是否超出数组容量
            throw new ConcurrentModificationException();
        cursor = i + 1;//指向下一个元素
        return (E) elementData[lastRet = i];//将lastRet更新并返回相应元素
    }
    

    遍历的方法:

    public void forEachRemaining(Consumer<? super E> consumer) {
    	Objects.requireNonNull(consumer);//Consumer消费者接口实现遍历的具体操作
        final int size = ArrayList.this.size;//获取数据的数目
        int i = cursor;//获取当前的索引
        if (i >= size) {//判断是否越界
        	return;
        }
        final Object[] elementData = ArrayList.this.elementData;//获取顺序表的数据
        if (i >= elementData.length) {//判断容量的越界
        	throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {//检查遍历边界和fail-fast
        	consumer.accept((E) elementData[i++]);//循环的进行遍历并将数据传给消费者
        }
        cursor = i;//刷新索引
        lastRet = i - 1;
        checkForComodification();//检查fail-fast
    }
    

    Consumer接口中的accept方法就是一个没有返回值的函数:

    void accept(T t);
    

    同样在ArrayList中也提供了相关的遍历方法:

    public void forEach(Consumer<? super E> action) {
    	Objects.requireNonNull(action);//保证action非空
        final int expectedModCount = modCount;//预先保存expectedModCount
        final E[] elementData = (E[]) this.elementData;//获取顺序表的数据
        final int size = this.size;
        //循环的进行遍历并将数据传给消费者
        for (int i=0; modCount == expectedModCount && i < size; i++) {
    	    action.accept(elementData[i]);
        }
        if (modCount != expectedModCount) {//检查fail-fast
        	throw new ConcurrentModificationException();
        }
    }
    

    获取

    ArrayList提供了获取Itr的方法;

    public Iterator<E> iterator() {
        return new Itr();
    }
    

    ListItr

    ListItr继承自Itr,实现了ListIterator接口。

    构造函数

    ListItr(int index) {
        super();
        cursor = index;
    }
    

    相比与Itr,ListItr提供了对遍历位置的初始设置。

    索引与取值

    关于索引的操作,ListItr实现了对于表的双向遍历

    public boolean hasPrevious() {//边界判断,判断是否是表头
    	return cursor != 0;
    }
    
    public int nextIndex() {//返回下一个元素的索引
    	return cursor;
    }
    
    public int previousIndex() {//返回上一个元素的索引(双向)
    	return cursor - 1;
    }
    

    取值的操作,增加了一个对于上一元素的取值:

    public E previous() {
    	checkForComodification();//检查fail-fast
        int i = cursor - 1;//获取上一个元素的索引
        if (i < 0)//边界判断
        	throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;//获取顺序表的数据
       	if (i >= elementData.length)//容量边界判断
        	throw new ConcurrentModificationException();
        cursor = i;//刷新索引
        return (E) elementData[lastRet = i];//返回上一元素
    }
    

    修改与插入

    修改操作,使用的是ArrayList中的set方法,不改变modCount的值。

    public void set(E e) {
    	if (lastRet < 0)//边界判断
        	throw new IllegalStateException();
        checkForComodification();//检查fail-fast
    	try {
        	ArrayList.this.set(lastRet, e);//使用ArrayList的set方法来修改
        } catch (IndexOutOfBoundsException ex) {//异常来自ArrayList的set中的检查
        	throw new ConcurrentModificationException();
        }
    }
    

    插入操作,使用ArrayList中的add方法插入数据。

    public void add(E e) {
    	checkForComodification();//检查fail-fast
    	try {
    		int i = cursor;
    		ArrayList.this.add(i, e);//使用ArrayList中的add方法插入数据
            cursor = i + 1;//更新索引
            lastRet = -1;
            expectedModCount = modCount;//更新expectedModCount
    	} catch (IndexOutOfBoundsException ex) {//异常来自ArrayList中的add方法对边界的检查
            throw new ConcurrentModificationException();
        }
    }
    

    获取

    ArrayList提供了获取ListIterator的方法;

    public ListIterator<E> listIterator() {
    	return new ListItr(0);//默认由起始位置开始遍历
    }
    

    ArrayListSpliterator

    ArrayListSpliterator实现了Spliterator接口,相比于前两个迭代器,ArrayListSpliterator实现了分割迭代。

    构造方法

    private final ArrayList<E> list;//内部的引用
    private int index; // 当前索引的位置
    private int fence; // 边界位置
    private int expectedModCount; // 用于检测fail-fast
    //构造函数就是将相应的参数设置初值
    ArrayListSpliterator(ArrayList<E> list, int origin, int fence, int expectedModCount) {
        this.list = list; 
        this.index = origin;
        this.fence = fence;
    	this.expectedModCount = expectedModCount;
    }
    

    边界检测

    在进行迭代遍历的时候是以index与fence进行范围约束的。

    private int getFence() { //用于获取要进行操作的顺序表的范围
    	int hi;//用于存储顺序表的截至范围
        ArrayList<E> lst;
        if ((hi = fence) < 0) {//fence初始时为-1时表示未进行边界检测
        	if ((lst = list) == null)//之后根据传入的顺序表的大小来确定边界
            	hi = fence = 0;//表为空时
            else {//表不为空expectedModCount取表中的modCount
            	expectedModCount = lst.modCount;
                hi = fence = lst.size;
            }
        }
        return hi;//返回当前表的边界位置
    }
    

    超前处理

    传入一个消费者的接口,用其来处理当前索引之后一个元素。

    public boolean tryAdvance(Consumer<? super E> action) {
    	if (action == null)//检查消费者接口非空
        	throw new NullPointerException();
        int hi = getFence(), i = index;//设置边界
        if (i < hi) {//越界检查
        	index = i + 1;//指向下一个元素
            E e = (E)list.elementData[i];//取元素
            action.accept(e);//传给消费者使用
        	if (list.modCount != expectedModCount)//检测fail-fast
            	throw new ConcurrentModificationException();
        	return true;
        }
        return false;//越界则返回false
    }
    

    分割处理

    ArrayListSpliterator提供了划分的方法可以将相应范围的ArrayListSpliterator等分返回第位的新的ArrayListSpliterator。

    public ArrayListSpliterator<E> trySplit() {
    	int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid) ? null : new ArrayListSpliterator<E>(list, lo, index = mid, expectedModCount);
    }
    

    遍历

    ArrayListSpliterator与其他的迭代器一样提供了遍历的接口。

    public void forEachRemaining(Consumer<? super E> action) {
    	int i, hi, mc;
        ArrayList<E> lst; Object[] a;
        if (action == null)//检查消费接口非空
        	throw new NullPointerException();
        if ((lst = list) != null && (a = lst.elementData) != null) {//迭代数据的非空判断
            if ((hi = fence) < 0) {//设置遍历边界与expectedModCount
            	mc = lst.modCount;
                hi = lst.size;
            }
        	else
            	mc = expectedModCount;
            if ((i = index) >= 0 && (index = hi) <= a.length) {//进行边界的判断
            	for (; i < hi; ++i) {//进行遍历操作将数据送到消费者的accept方法
                	E e = (E) a[i];
                    action.accept(e);
               	}
                if (lst.modCount == mc)//检查fail-fast
                    return;
            }
        }
    	throw new ConcurrentModificationException();//如果出现fail-fast抛出异常
    }
    

    其余函数

    除此之外,还有两函数estimateSize表示还有多少的元素需要遍历,characteristics用于返回Spliterator的特征为ORDERED,SIZED与SUBSIZED。

    public long estimateSize() {
    	return (long) (getFence() - index);
    }
    
    public int characteristics() {
    	return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
    }
    /*Spliterator接口中有以下定义
    public static final int ORDERED    = 0x00000010;//元素是有序的(每一次遍历结果相同)
    public static final int DISTINCT   = 0x00000001;//元素不重复
    public static final int SORTED     = 0x00000004;//元素是按一定规律进行排列(指定比较器)
    public static final int SIZED      = 0x00000040;//元素数量确定
    public static final int NONNULL    = 0x00000100;//表示迭代器中没有null元素
    public static final int IMMUTABLE  = 0x00000400;//表示迭代器中元素不可变
    public static final int CONCURRENT = 0x00001000;//表示迭代器可以多线程操作
    public static final int SUBSIZED   = 0x00004000;//表示子Spliterators都具有SIZED特性
    */
    

    获取

    ArrayList提供了获取Spliterator的方法;

    public Spliterator<E> spliterator() {
    	return new ArrayListSpliterator<>(this, 0, -1, 0);
    }
    
  • 相关阅读:
    SHAREPOINT2007 文档库中通过EMAIL发送文档URL为乱码的解决方法
    ReadTrace
    实战分区表:SQL Server 2k5&2k8系列
    mssql 如何创建跟踪
    SQL Server自定义异常的使用raiserror
    SQL Server 2008内存及I/O性能监控
    实战 SQL Server 2008 数据库误删除数据的恢复
    MSSQL常用性能測試語句
    sqlserver 2008 设置了镜像 如何收缩日志文件
    复制订阅错误处理。
  • 原文地址:https://www.cnblogs.com/yanzs/p/13788241.html
Copyright © 2011-2022 走看看