解析java.util集合类源码(SubList)

来源:互联网 发布:cad mac中文破解版 编辑:程序博客网 时间:2024/05/17 02:12

SubList类


在1.6和1.7的api中没有给出SubList的说明,只有subList方法,返回一个List列表的子类列表

public List<E> subList(int fromIndex,int toIndex)
但在jdk的源码中,你可以搜到SubList,它在AbstractList类下,却不是内部类

先看下SubList的结构

SubList继承AbstractList抽象类,AbstractList实现了List接口,所以SubList说到底就是一个List的实现类,主要用于返回List的视图,这个视图是原List对象中的一部分,确实是一部分,直接将原List对象引用到新的子视图的List,对子视图进行改变,原List对象也会随之改变

下面就解释为什么会造成这种情况

首先看SubList中的属性域

SubList有4个自己的属相域和1个继承自AbstractList的modCount

private AbstractList<E> l;private int offset;private int size;private int expectedModCount;
AbstractList l 就是存放母List的那个引用,当List对象(本编中指类似ArrayList的实现List的对象)调用subList方法时候,会调用abstractList中的subList的实现

 //AbstractList中的subList方法 public List<E> subList(int fromIndex, int toIndex) {        return (this instanceof RandomAccess ?                new RandomAccessSubList<E>(this, fromIndex, toIndex) :                new SubList<E>(this, fromIndex, toIndex));    }
这里this是多态的,指的是那个外部调用subList方法的那个List对象,先判断this是否实现了RandomAccess接口,实现返回RandomAccessSublist否则返回SubList,通过构造方法将this传给SubList中l属性,在介绍构造方法时细说

int offset 开始子List在母List中的开始位置

int size 记录大小 = toIndex - fromIndex

int expectedModCount  列表的中元素添加、删除的次数,子列表的同步使用

 SubList(AbstractList<E> list, int fromIndex, int toIndex) {        if (fromIndex < 0)            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);        if (toIndex > list.size())            throw new IndexOutOfBoundsException("toIndex = " + toIndex);        if (fromIndex > toIndex)            throw new IllegalArgumentException("fromIndex(" + fromIndex +                                               ") > toIndex(" + toIndex + ")");        l = list;        offset = fromIndex;        size = toIndex - fromIndex;        expectedModCount = l.modCount;    }

SubList的构造方法

做3个对fromIndex和toIndex的检查后,将list 付给l,fromIndex付给offset,原list对象的modCount付给expectedModCount

其实就是将原来的list对象放到SubList中,同时将子列表的开始下标和结束下标付给SubList


public E set(int index, E element) {        rangeCheck(index);        checkForComodification();        return l.set(index+offset, element);    }
修改子列表中下标为index 的元素,同时也将原list中的元素修改,rangeCheck检查下标越界,checkFormComodification判断list是否同步

同步:在List对象返回Iterator迭代器后,不能够对List 对象进行(添加、删除)操作,否则modCount++,这时候modeCount不等于expectedmoCount。

这是在一般的List中,但是在SubList中不但要考虑迭代器和List对象的同步问题,还有母列表和子列表的同步问题,下面的checkFormComodification方法将l的modCount和subList中的expectedModCount比较,当List对象通过subList(fromIndex,toIndex)方法获取到SubList后,母List对象又调用add方法,这时母List对象中的modCount++,但子列表中的expectedModCount并没有变,造成不同步抛出异常

private void rangeCheck(int index) {        if (index<0 || index>=size)            throw new IndexOutOfBoundsException("Index: "+index+                                                ",Size: "+size);    }

private void checkForComodification() {        if (l.modCount != expectedModCount)            throw new ConcurrentModificationException();    }

检查母列表和子类表的同步


public E get(int index) {        rangeCheck(index);        checkForComodification();        return l.get(index+offset);    }
和set方法类似,母列表开始下标是0,子列表开始下标是offset,所以获取母列表中的元素时,应加上offset

public int size() {        checkForComodification();        return size;    }
返回子列表的size

public void add(int index, E element) {        if (index<0 || index>size)            throw new IndexOutOfBoundsException();        checkForComodification();        l.add(index+offset, element);        expectedModCount = l.modCount;        size++;        modCount++;    }
将元素添加到子列表的index位置,首先检查是否同步,调用母列表的add(index,e)方法,这时候母列表的modCount会增加,更新子列表中用来判断和母列表同步的变量expectedModCount,子列表的size++,子列表的modCount++

public E remove(int index) {        rangeCheck(index);        checkForComodification();        E result = l.remove(index+offset);        expectedModCount = l.modCount;        size--;        modCount++;        return result;    }
remove方法与add(index,e)方法类似,调用母列表的remove(index)方法
protected void removeRange(int fromIndex, int toIndex) {        checkForComodification();        l.removeRange(fromIndex+offset, toIndex+offset);        expectedModCount = l.modCount;        size -= (toIndex-fromIndex);        modCount++;    }
与add(index,e)方法相似,调用母列表的removeRange(fromIndex,toIndex)方法

public boolean addAll(Collection<? extends E> c) {        return addAll(size, c);    }    public boolean addAll(int index, Collection<? extends E> c) {        if (index<0 || index>size)            throw new IndexOutOfBoundsException(                "Index: "+index+", Size: "+size);        int cSize = c.size();        if (cSize==0)            return false;        checkForComodification();        l.addAll(offset+index, c);        expectedModCount = l.modCount;        size += cSize;        modCount++;        return true;    }
类似

 public Iterator<E> iterator() {        return listIterator();    }
重写了iterator方法,在SubList中不管调用iterator方法,还是listIterator方法都返回的是ListIterator


public ListIterator<E> listIterator(final int index) {        checkForComodification();        if (index<0 || index>size)            throw new IndexOutOfBoundsException(                "Index: "+index+", Size: "+size);        return new ListIterator<E>() {            private ListIterator<E> i = l.listIterator(index+offset);            public boolean hasNext() {                return nextIndex() < size;            }            public E next() {                if (hasNext())                    return i.next();                else                    throw new NoSuchElementException();            }            public boolean hasPrevious() {                return previousIndex() >= 0;            }            public E previous() {                if (hasPrevious())                    return i.previous();                else                    throw new NoSuchElementException();            }            public int nextIndex() {                return i.nextIndex() - offset;            }            public int previousIndex() {                return i.previousIndex() - offset;            }            public void remove() {                i.remove();                expectedModCount = l.modCount;                size--;                modCount++;            }            public void set(E e) {                i.set(e);            }            public void add(E e) {                i.add(e);                expectedModCount = l.modCount;                size++;                modCount++;            }        };    }
listIterator方法返回的是一个ListIterator匿名类

 private ListIterator<E> i = l.listIterator(index+offset);

这个ListIterator匿名类本身操作的也是母列表中的listiterator,用i来存储这个参数

其他方法基本都是通过i这个ListIterator来对迭代进行操作,方法类似AbstractList中ListIterator,这个迭代不熟悉请看上一篇博客


更好的理解SubList类,我们可以写段程序,Debug他内部的调用过程


母列表和子列表的不同步:

    @Test    public void test(){                ArrayList list = new ArrayList();        list.add("ee");        list.add("ee");        list.add("eee");                List subList = list.subList(0, 2);        list.add("ee");//母列表元素增加,母列表中的modCount增加,破坏母列表和子列表的同步        Iterator it2 = subList.iterator();//报错,子列表和母列表不同步            }
这段代码测试子列表与母列表不同步的问题

list是一个普通的ArrayList对象,subList一个从list中截取的一段子列表,我们从最开始进行debug跟踪,当list.subList()->AbstractList.subList ->这时候new SubList(ps:RandomAccessSubList也是SubList,不细说),将subList中的参数初始化,重要的同步参数expectedModCount = l.modCount = 3,此时SubList中的expectedModCount = 3,list.subList走完,此时 expectedModCount = l.modCount = 3

再看主程序中下条代码list.add(e)->ArrayList.add(e)->ArrayList.ensureCapacity(int minCapacity),这方法中modCount++,这个modCount是母列表中的modCount也就是SubList中 的l.modCount,然后添加元素,list.add(e)走完,此时l.modCount = 4 ,SubList中的expectedModCount =3

当subList.iterator()->SubList.iterator()->SubList.listIterator()->SubList.checkForComodification()这里有段代码

private void checkForComodification() {        if (l.modCount != expectedModCount)            throw new ConcurrentModificationException();    }
用母列表的modCount和SubList中expectedModCount比较相等,expectedModCount是在new SubList()时候被初始化的= 3 ,而l.modCount在list.add()时候++了=4,造成了母列表和子列表的不同步


列表与迭代器的不同步:

修改一下刚才的代码


@Testpublic void test(){ArrayList list = new ArrayList();list.add("ee");list.add("ee");list.add("eee");List subList = list.subList(0, 2);Iterator it2 = subList.iterator();subList.add("在这里会修改母list中的modCount");it2.next();//这会报错,原因母列表的modCount与迭代器中的expecedModCount值不同Iterator it = list.listIterator();list.add("在这里会修改list中的modCount");it.next(); //这会报错,原因母列表的modCount与迭代器中的expecedModCount值不同}

subList方法前面有讲,大体过程和上述相同

 当subList.iterator时,在SubList中的iterator在检查同步之后,返回一个ListIterator匿名类,其中有一个参数private ListIterator<E> i = l.listIterator(index+offset);在初始化这匿名类时候,调用母列表的listIterator方法返回一个母列表的迭代器,初始化这个迭代器时,有一个int expectedModCount = modCount,这个expectedModCount是母列表迭代器中的,与SubList中的那个无关,此时母列表迭代器ListIterator中的expectedModCount = 母列表的modCount = 3

当subList.add()时候->AbstractList.add(e)->SubList.add(index,e)这里调用母列表的add方法->ArrayList.add在这里会将母列表中的modCount++ = 4,然后添加元素
再看迭代时的代码it2.next()

it2.next()调用ListIterator匿名类中的next()方法(ps:it2本身就是那个被new出来的匿名类)

public E next() {                if (hasNext())                    return i.next();                else                    throw new NoSuchElementException();            }
这时使用是母列表的迭代器的next方法,在next方法中checkForComodification()检查同步此时的母列表的modCount = 4,但母列表迭代器的expectedModCount =3 不同步,报错


所以说在List中使用subList要小心,因为本身SubList这个类就是原List的一个视图,改变SubList必定会改变原List


0 0