ArrayList中你分得清楚size与capacity的区别吗?

JDK版本: openJDK 1.8

问题

在ArrayList中,size跟capacity是一样的东西吗?

实验

在ArrayList中有这么一个构造函数可以让我们指定初始的容量(Capacity)。

public ArrayList(int initialCapacity);

然后我们可以利用这个构造函数来创建一个ArrayList并在构造函数里指定初始容量为10。接着尝试在下标为9的位置插入一个数据。

ArrayList list = new ArrayList<Integer>(10);
int index = 9;
int data = 0;
list.add(index, data);

原本我的想法是,既然我们的初始容量可以容纳10个数据,那么在下标为9的位置插入一个数据应该是没有问题的。然而程序却抛出了一个异常错误:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 9, Size: 0

根据这个异常错误信息,往下标9的位置插入数据超越了数组的界限。

追根究底

这就很奇怪了,明明我们ArrayList的Capacity是10,为什么下标9会越界呢?

查看ArrayList的构造函数

为了寻找原因,我打开了ArrayList的源代码,首先我们来看一下在ArrayList中可以指定Capacity的构造函数:

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    	// 我们传入的参数为10,因此 ArrayList 会初始化一个大小为 10 的 Object 数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

由于我们传入的initialCapacity参数为10,因此ArrayList会初始化一个大小为10的 Object 数组来储存我们的数据。

到此为止看上去都很合理,ArrayList的底层数组实际上是足够大到可以让我们往下标9的位置插入数据,那么问题会是出在哪里呢?

查看ArrayList的add(index, data)函数

我们的下一步就是要查看ArrayList中可以往指定下标位置插入数据的add(index, data)函数。

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++;
}

从源代码中,我们发现有一个函数看起来很可疑:rangeCheckForAdd(index)

根据名字来看似乎是在确认数组是否越界。ctrl + 左键 点击该函数查看源代码:

private void rangeCheckForAdd(int index) {
	// 原来是根据 size 来判断数组是否越界
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

从代码中可以看出,ArrayList是使用size属性来确定数组是否越界,并非capacity。

那么size属性是从哪里来的呢?

如果我们再看回add(index, data)函数,就会发现ArrayList在add操作的最后做了size++的操作

让我们再来看看add(E e)函数的源代码:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e; // 在末尾插入数据之后,size++
    return true;
}

可以看到,add(E e)函数会在数组的末尾插入数据,并进行size++的操作。

也就是说,只有当插入新数据的时候,size才会往上提升。

同样的,如果我们检查 remove(int index) 的源代码,也会发现它会进行size–的操作。

因此,size表示的是数组中元素的数量,并非数组的容量

总结

在检查了ArrayList的源代码之后,我们可以总结出以下结论:

  • 在ArrayList中,size与capacity是不同的概念
  • size指的是ArrayList中元素的数量
  • capacity指的是在ArrayList底层实现中Object数组的大小,也可以理解为ArrayList的容量
  • ArrayList是使用size来判断数组是否越界

总结图

这张总结图大家可以收藏一下,用作复习。

arraylist

如果大家觉得文章有用的话,请帮忙点个赞,谢谢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章