ArrayList的底层使用数组进行存放元素的,底层实现是通过一个对象数组Object[]来存放元素的,由于是对象数组,所以只能存放对象,并可以存放任何对象。而数组中存放了对象的引用,所以ArrayList中也存放对象的引用,ArrayList只能存放对象。
ArrayList有3个构造函数:
构造一个初始容量为 10 的空列表。 |
构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。 |
构造一个具有指定初始容量的空列表。 |
我们通常使用ArrayList时会使用其不带参数的构造函数。源码如下:
privatetransientObject[] elementData;
publicArrayList(intinitialCapacity) {
super();
if(initialCapacity < 0)
thrownewIllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = newObject[initialCapacity];
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
publicArrayList() {
this(10);
}
其底层实现是:
不带参数的构造函数会调用带参数的构造函数。
不带参数的构造函数会默认传递一个容量为10参数,在带参数的构造函数中会声明一个长度为10的Object数组,也就是说使用不带参的构造函数时使用this关键字调用有参的构造函数并设置初始容量大小为10。
我们学习到这,细心的读者或者初学者可能会发现一个问题。当我们使用ArrayList时,我们可以无限的往里添加元素,这是为什么呢?它底层不是由数组实现的,使用无参构造方法时系统会默认提供默认参数10,而使用有参构造函数时我们会指定大小,我们都是知道使用数组时当添加的元素个数大于数组的初始化长度时会报数组下标越界异常,那么ArrayList为什么不会呢?说到这里,可能已有读者知道ArrayList可能会自动为其扩容,没错,的确是这样的。我们来看看源码:
ArrayList中常用的add方法:
源码:
publicbooleanadd(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
returntrue;
}
使用ensureCapacity(int minCapacity)方法简单的理解就是为ArrayList扩容。
java api中这样解释:
public void ensureCapacity(int minCapacity)
ArrayList
参数: minCapacity
publicvoidensureCapacity(intminCapacity) {
modCount++;
intoldCapacity = elementData.length;
if(minCapacity > oldCapacity) {
Object oldData[] = elementData;
intnewCapacity = (oldCapacity * 3)/2+ 1;
if(newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
通过以上分析我们得出结论:
ensureCapacity方法它和底层数组的大小做比较,如果小于底层数组的长度则什么也不做;当大于底层数组长度时,它将原数组的大小进行扩容,大小为原数组的3/2+1;然后使用Arrays.copyOf方法,将原数组的元素拷贝到新指定大小的数组里,实现数组的扩容。