Java之ArrayList源码分析(第八篇:内部类-ListItr)

(注意:本文基于JDK1.8) 

Java之ArrayList源码分析(第八篇:内部类-ListItr)_第1张图片

ArrayList中重写了基类AbstractList的两个listIterator方法,这两个方法的返回值都是ListItr对象,ListItr类是作为增强型的迭代器而设计的,接下来就一探究竟!

 

0、ListItr的类结构

    private class ListItr extends Itr implements ListIterator {
       …………省略很多代码………… 
}

ListItr类定义在ArrayList的内部,作为ArrayList的普通内部类,ListItr扩展了基础的顺序迭代器类Itr(详细介绍),并实现了ListIterator接口(见本文1号知识点)

 

1、ListIterator接口介绍

public interface ListIterator extends Iterator {
   …………省略很多代码…………
}

ListIterator接口定义了作为线性表迭代器的功能(能力),ListIterator扩展了普通迭代器Iterator接口的功能(具备的能力),看下ListIterator定义了哪些方法

Java之ArrayList源码分析(第八篇:内部类-ListItr)_第2张图片

根据上文截图,在Iterator接口的基础功能之上,又新增了add、hasPrevious、nextIndex、previous、previousIndex、set方法

 

2、ListItr对象的创建

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

ListItr类接受一个参数的构造方法,传入的index值代表线性表的下标值,在构造方法的内部首先调用了父类的构造方法(见本文3号知识点),接着将传入的参数index赋值给ListItr对象持有的实例变量cursor保存起来,注意实例变量cursor是定义在父类Itr中的,这就是ListItr对象的创建,当然实例变量也执行了初始化

 

3、父类Itr的构造方法

        Itr() {}

父类Itr的构造方法没有做任何初始化的工作(注意:Itr类中定义的实例变量,在Itr类加载到JVM时已经完成了初始化)

 

4、ListItr中实现的hasPrevious方法

        public boolean hasPrevious() {
            return cursor != 0;
        }

hasPrevious方法用于判断当前线性表遍历过程中是否存在上一个元素,当ListItr对象持有的实例变量cursor不等于0时,我们则认为当前ArrayList中仍然存在上一个元素没有遍历过,当cursor等于0时,已经是最左侧的元素,或者说是第一个元素,当然不会有上一个元素的存在

 

5、ListItr中实现的nextIndex方法

        public int nextIndex() {
            return cursor;
        }

nextIndex方法返回当前迭代器遍历的当前元素下标cursor,在迭代器中,cursor的初始值是0,第一次调用Itr中的next方法时,返回的正是cursor值为0的元素

 

6、ListItr中实现的previousIndex方法

        public int previousIndex() {
            return cursor - 1;
        }

previousIndex方法返回迭代器即将遍历元素的上一个元素,在实现上直接使用cursor-1即可得到上一个元素,因为cursor值代表的是迭代器调用next返回的元素下标

 

7、ListItr中实现的previousIndex方法

        public E previous() {
            checkForComodification();
            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];
        }

previous方法是ListItr迭代器对象用来返回上一个元素对象的方法

a、首先调用了checkForComodification方法(见本文8号知识点)检查ArrayList持有的元素数量是否发生过改变

b、检查通过后,先获取ListItr对象持有的cursor值,接着将cursor值减去1,临时交给局部变量i保存,如果i的值小于0,说明已经没有上一个元素,此时会抛出一个NoSuchElementExcepton对象(使用本文中4号知识点提及的方法即可避免这种情况)

c、接着获取当前ArrayList对象持有的数组对象elementData并赋值给局部变量elementData保存,然后再次对即将遍历的局部变量i与数组对象elementData的长度进行对比,如果即将遍历的元素下标i大于等于数组elementData的长度,说明在遍历过程中,ArrayList变更了元素数量,此时就会抛出ConcurrentModificationException

d、检查再次通过后,则先更新当前遍历元素的下标cursor为局部变量i的值,在方法的最后则是执行return语句,先把返回的元素下标记录在ListItr对象持有的实例变量lastRet中(lastRet定义在父类Itr中),接着从数组对象elementData中的下标lastRet中将元素对象返回调用者

 

8、定义在父类Itr中的checkForComodification方法

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

checkForComodification方法的功能是fail-fast机制的检查者,它检查当前线性表在遍历过程中,是否发生过变更元素的行为(添加、删除、清除),每次元素改变,ArrayList对象持有的modCount都会+1的操作(注意:modCount定义在ArrayList的父类AbstractList中),而expectedModCount则是ListItr对象持有的实例遍历,它在ListItr对象创建时,存储了ArrayList最初的modCount值,通过对比ArrayList对象持有的modCount与ListItr对象持有的expectedModCount值,如果不相等,则会直接抛出ConcurrentModificationException对象!

 

9、ListItr中实现的set方法

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

set方法可以改变最后一次通过next或者previous方法返回的元素对象,或者说它可以变更lastRet下标处的元素对象

首先做了两个检查、一个是检查lastRet下标值不能小于0,否则就抛出IllegalStateException对象,另一个则是通过checkForComodification方法(见本文8号知识点)的检查,在try ..catch...代码块中,则是通过ArrayList对象的set方法进行元素的变更,set方法接受lastRet、以及传入的元素对象e,set方法可能会抛出IndexOutOfBoundsException,catch代码块中会捕获该异常,然后再次抛出一个ConcurrentModificationException对象(因为只有ArrayList中的元素数量改变了,才可能出现IndexOutOfBoundsException的问题)

Java之ArrayList源码分析(第八篇:内部类-ListItr)_第3张图片

 

10、ListItr实现的add方法

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

add方法可以添加一个元素到当前ArrayList对象中,它接受一个参数类型为E的对象

a、首先调用checkForComodification方法(见本文8号知识点)做一次检查

b、检查通过后,则是try...catch...代码块的执行,先将ListItr对象持有的cursor赋值给局部变量i,真正添加元素的工作是通过ArrayList的add方法完成的,add方法接受下标值i、以及元素对象e

c、元素添加完成后,将新增元素的数量+1,并赋值给ListItr持有的cursor,将ListItr记录的最后遍历返回的元素下标lastRet更新为初始值-1,说明迭代器上一次并没有进行遍历行为,而是其他行为如添加、删除、更新元素

d、ArrayList添加完元素后,ArrayList对象持有的modCount已经增加了1,此时就需要将ListItr对象持有expectedModCount重新赋值一次

e、ArrayList的add方法可能会抛出IndexOutBoundsException,此处做了捕获处理,捕获之后再次抛出ConcurrentModificationException对象,因为add方法抛出IndexOutOfBoundsException的根本原因是ArrayList的元素数量在某一刻改变了(进一步说明ArrayList无法在多线程环境下使用)

 

总结

a、ListItr比Itr迭代器新增加向前遍历元素的能力,这是通过hasPrevious、previousIndex、previous方法来实现的

b、ListItr也比Itr迭代器新增了添加元素的能力,add方法实现了该功能(见本文10号知识点)

c、ListItr比Itr新增了修改元素的能力,set方法实现了该功能(见本文9号知识点)

你可能感兴趣的:(Java,综合)