记录一次执行ArrayList的add方法异常细节复盘| Java Debug 笔记

524 阅读2分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接

image.png

前言

看到这个问题的时候,一眼看去,这么简单的问题,为何会问。其实,转眼一想,是的,这个还是很具有迷惑性的问题。对于初学者,来说,这块确实容易迷失。很多人不理解。

那么,我们就来看看这个问题,对它进行一次深入的解剖!

问题复现

定义一个初始化大小为10的集合

ArrayList<Integer> arr=new ArrayList<Integer>(10);

使用集合方法进行添加元素

arr.add(5,10)

我们看一下结果

image.png

迷惑的地方出现了,为啥会报越界呢?我们明明已经初始化了集合的大小为10,add方法,添加的索引为5,还不到10。

估计初学者已经懵逼,我们就来详细看看咋回事。

问题解析

首先,我们来看集合的add方法,是怎么定义的

/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 *
 * @param index index at which the specified element is to be inserted
 * @param element element to be inserted
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

方法,确实提供了两个参数,索引位置,元素值。

通过了解注释,我们可以看到,将指定的元素插入此列表中的指定位置。 将当前在该位置的元素(如果有)和任何后续元素右移(将其索引添加一个)

/**
 * A version of rangeCheck used by add and addAll.
 */
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

观察方法rangeCheckForAdd

我们,可以了解到,该方法,判断的是index和size的比较

/**
 * The size of the ArrayList (the number of elements it contains).
 *
 * @serial
 */
private int size;

那么,我们看到之前异常截图,显示Size为0,这是为啥来,我们好像已经初始化了大小。

看来,问题,在于初始化。我们就来看看,我们自定义集合时,到底干了点什么。

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

我们发现,初始化时,指定的是initialCapacity 初始化容量大小,看出来,并不是size大小。

问题明朗了,我们混淆了Size和Capacity的概念。

问题总结

size 是集合包含元素的数量

capacity 是定义集合能够包含元素的数量

初学者,需要更细心的观察源码的实现。