ArrayList源码解析之subList

本文通过实例分析ArrayList的subList方法,探讨其用法和潜在风险,包括子列表操作影响父列表以及使用父列表方法修改集合导致的ConcurrentModificationException异常。通过对源码的解读,揭示subList内部实现及边界检查机制。

ArrayList的 subList ( int fromIndex, int toIndex ) 方法执行结果是获取ArrayList的一部分,返回的是ArrayList的部分视图。《阿里巴巴Java开发手册》中对subList方法的使用有规定:
阿里开发手册
首先通过一个例子,初步了解subList的用法和易出错的地方:

import java.util.ArrayList;
import java.util.List;

public class SubListDemo {

	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			list.add(i);
		}
		System.out.println("原:" + list); 
		
		List<Integer> subList = list.subList(0, 5);
		System.out.println("子:" + subList); 

		// subList的add()方法
		subList.add(3, 21);

		// 输出原集合
		System.out.println("==============subList的add()方法执行结果====================");
		System.out.println("原:" + list); 
		// 输出subList
		System.out.println("子:" + subList); 

		// ArrayList的add()方法
		list.add(31);
		// 输出原集合
		System.out.println("==============ArrayList的add()方法执行结果====================");
		System.out.println("原:" + list); 
		// 输出subList
		System.out.println("子:" + subList);

	}

}

程序的执行结果为:
执行结果从结果得出:

  • 对子类subList的操作会反映到父类中。
  • 使用父类的方法(能改变modCount值的方法)修改集合会导致子类的遍历抛出 ConcurrentModificationException 异常。

程序的执行结果也验证了阿里手册的规定,下面,通过源码解析更加清晰的理解这两条规定的用意。

ArrayList 的 subList 方法返回的一个内部类 SubList,返回的 List 中,下标范围在[fromIndex, toIndex) 之间:

    public List<E> subList(int fromIndex, int toIndex) {
    	// 参数检查
        subListRangeCheck(fromIndex, toIndex, size);
        // 内部类SubList,this为父类的引用,0 表示父类下标偏移量
        return new SubList(this, 0, fromIndex, toIndex);
    }

subListRangeCheck(fromIndex, toIndex, size);方法检查两个下标是否合规:

    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

SubList 内部类,继承了 AbstractList 类,并实现了 RandomAccess 接口,支持随机读取:

    /*
     * SubList返回的视图是由父类集合支持的,因此是非结构化的
     * 所以,对SubList子集合进行操作,也会修改父类的集合。
     * SubList类中,每个public方法(除了subList()方法)都调用checkForComodification()
     * 用于判断父类集合是否被修改
     * 所以,如果直接使用父类方法修改集合,则SubList子类的遍历、增加、删除等操作都会抛出异常
     */
    private class SubList extends AbstractList<E> implements RandomAccess {
        // 父类的引用
        private final AbstractList<E> parent;
        /*
        * 父类集合中的位置,如果使用SubList中的subList方法,
        * 则此时父类为SubList类,不是ArrayList
        */
        private final int parentOffset;
        // 子类List在父类 ArrayList 中的下标位置
        private final int offset;
        // 视图集合的size
        int size;
        
        // 构造方法,参数offset表示父类集合的下标偏移量
        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;
        }
        
        public E set(int index, E e) {
        	// 检查下标是否越界,这里index的值在[0, this.size)之间,size = toIndex - fromIndex
            rangeCheck(index);
            // 检查是否有其他线程修改了父类集合
            checkForComodification();
            E oldValue = ArrayList.this.elementData(offset + index);
            // 调用父类方法替换元素,所以本质上还是在父类集合中替换元素
            ArrayList.this.elementData[offset + index] = e;
            return oldValue;
        }

        public E get(int index) {
        	// 检查下标是否越界,这里index的值在[0, this.size)之间
            rangeCheck(index);
            checkForComodification();
            // 调用父类方法获取元素
            return ArrayList.this.elementData(offset + index);
        }

        public int size() {
            checkForComodification();
            return this.size;
        }

        public void add(int index, E e) {
            // 检查下标,index的值在[0, this.size]之间
            rangeCheckForAdd(index);
            checkForComodification();
            // 使用父类方法添加元素,下标位置为parentOffset + index, 在父类集合添加元素。
            parent.add(parentOffset + index, e);
            // 父类add()方法修改了modCount的值,更新subList的modCount值
            this.modCount = parent.modCount;
            this.size++;
        }
        
        // 根据下标移除元素
        public E remove(int index) {
            rangeCheck(index);
            checkForComodification();
            E result = parent.remove(parentOffset + index);
            this.modCount = parent.modCount;
            this.size--;
            return result;
        }

        // 移除指定区间的元素
        protected void removeRange(int fromIndex, int toIndex) {
            checkForComodification();
            parent.removeRange(parentOffset + fromIndex,
                               parentOffset + toIndex);
            this.modCount = parent.modCount;
            this.size -= toIndex - fromIndex;
        }

        public boolean addAll(Collection<? extends E> c) {
            return addAll(this.size, c);
        }

        public boolean addAll(int index, Collection<? extends E> c) {
            rangeCheckForAdd(index);
            int cSize = c.size();
            if (cSize==0)
                return false;

            checkForComodification();
            parent.addAll(parentOffset + index, c);
            this.modCount = parent.modCount;
            this.size += cSize;
            return true;
        }
        
        // subList 中迭代器使用ListIterator(),迭代器的源码已分析
        public Iterator<E> iterator() {
            return listIterator();
        }

        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;
                    // offset + index 的下标为此时subList中元素在父类集合中的位置
                    return (E) elementData[offset + (lastRet = i)];
                }

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

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

                @SuppressWarnings("unchecked")
                public void forEachRemaining(Consumer<? super E> consumer) {
                    Objects.requireNonNull(consumer);
                    final int size = SubList.this.size;
                    int i = cursor;
                    if (i >= size) {
                        return;
                    }
                    final Object[] elementData = ArrayList.this.elementData;
                    if (offset + i >= elementData.length) {
                        throw new ConcurrentModificationException();
                    }
                    while (i != size && modCount == expectedModCount) {
                        consumer.accept((E) elementData[offset + (i++)]);
                    }
                    // update once at end of iteration to reduce heap write traffic
                    // 与 Itr 中不同
                    lastRet = cursor = i;
                    checkForComodification();
                }

                public int nextIndex() {
                    return cursor;
                }

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

                public void remove() {
                    if (lastRet < 0)
                        throw new IllegalStateException();
                    checkForComodification();

                    try {
                        SubList.this.remove(lastRet);
                        cursor = lastRet;
                        lastRet = -1;
                        expectedModCount = ArrayList.this.modCount;
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }

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

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

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

                    try {
                        int i = cursor;
                        SubList.this.add(i, e);
                        cursor = i + 1;
                        lastRet = -1;
                        expectedModCount = ArrayList.this.modCount;
                    } catch (IndexOutOfBoundsException ex) {
                        throw new ConcurrentModificationException();
                    }
                }
                
                
                // 检查是否有多线程修改集合
                final void checkForComodification() {
                    if (expectedModCount != ArrayList.this.modCount)
                        throw new ConcurrentModificationException();
                }
            };
        }
        
        // 内部方法
        public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);
            return new SubList(this, offset, fromIndex, toIndex);
        }
        
        // 下标越界检查
        private void rangeCheck(int index) {
            if (index < 0 || index >= this.size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
        
        // 下标越界检查,仅add()方法和addAll()方法使用
        private void rangeCheckForAdd(int index) {
            if (index < 0 || index > this.size)
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
        	
        // 下标越界异常信息
        private String outOfBoundsMsg(int index) {
            return "Index: "+index+", Size: "+this.size;
        }
        
        // 是否有多线程修改集合
        private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }
        
        // 下篇文章讲解
        public Spliterator<E> spliterator() {
            checkForComodification();
            return new ArrayListSpliterator<E>(ArrayList.this, offset,
                                               offset + this.size, this.modCount);
        }
    }

关于 parentOffset 和 offset :
脑图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值