Java集合的subList方法分析

本文研究List集合的subList方法,测试方式为:新建一个集合,然后截取原集合的部分元素,然后去操作新集合和原集合来观察结果。

1.新集合中添加元素

public static void testSubList(){
        List list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(5);
        list.add(6);

        //截取集合
        List subList = list.subList(0, 3);
        System.out.println(subList);
        //截取的新集合添加元素
        subList.add(33);

        System.out.println(subList);
        System.out.println(list);
    }

结果为:

[1, 2, 3]
[1, 2, 3, 33]
[1, 2, 3, 33, 4, 5, 5, 6]

结论:我们会发现,虽然list 和 subList是两个不同的对象,但是我们在操作新集合时,发现原集合的数据也改变了,看一下源码:

        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }

这里的parent是原集合,sunList返回的实际上是原集合的部分视图,所以你去修改新集合时,实际是把原集合改了。

2.原始集合添加元素

    public static void testSubList2(){
        List list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(5);
        list.add(6);

        //截取集合
        List subList = list.subList(0, 3);
        System.out.println(subList);
        System.out.println(list);

        //原始集合添加元素
        list.add(1000);

        System.out.println(list);
        //System.out.println(subList);
    }

结果:

[1, 2, 3]
[1, 2, 3, 4, 5, 5, 6]
[1, 2, 3, 4, 5, 5, 6, 1000]

如果再去打印subList,会报错:

java.util.ConcurrentModificationException

3.报错分析

这个打印输出为什么会报错呢?因为在打印时,其实是在迭代元素然后拼接后打印输出,在迭代的时候,出现了这个错误,我们按照调用链详细的追溯一下源码:
打印对象

    public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

对象要toString()成字符串

    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

打印的是集合,那转字符串时要迭代出元素才行,得有个迭代器

    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }

获取迭代器

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

获取List集合的迭代器

    public ListIterator<E> listIterator() {
        return listIterator(0);
    }

迭代器内部

扫描二维码关注公众号,回复: 1527227 查看本文章
        public ListIterator<E> listIterator(final int index) {
            checkForComodification();
            rangeCheckForAdd(index);
            final int offset = this.offset;

            return new ListIterator<E>() {
                int cursor = index;
                int lastRet = -1;
                int expectedModCount = ArrayList.this.modCount;

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

                @SuppressWarnings("unchecked")
                public E next() {
                    checkForComodification();
                    int i = cursor;
                    if (i >= SubList.this.size)
                        throw new NoSuchElementException();
                    Object[] elementData = ArrayList.this.elementData;
                    if (offset + i >= elementData.length)
                        throw new ConcurrentModificationException();
                    cursor = i + 1;
                    return (E) elementData[offset + (lastRet = i)];
                }
                。。。。。。
        }        

迭代前要做个检查,看看集合有没有被改变,这两个值如果不相等,就抛出错误,这就是我们之前打印抛出的那个错误。

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

由于在截取新集合时,原来集合的的size是7,那么在你subList截取的新集合中,这个modCount也是7,现在,我们在原集合中添加了元素,那原来集合的modCount已经成8了,而这个截取获得的集合,modCount是7,比较后不相等,所以抛出了错误。

modCount是什么呢?下文:java modCount分析

猜你喜欢

转载自blog.csdn.net/weixin_39800144/article/details/80610616