介绍
ArraryList继承了List接口,它可以改变大小,支持快速访问,并且它的项可以是null
。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
}
数据结构
ArrayList的底层的数据结构是一个 「Object[] elementData」 数组。
通过 「int size」 变量来记录ArrayList中当前包含的元素个数。当通过 「add」 方法向ArrayList添加元素时,如果添加成功size会自动+1。使用「remove」方法删除元素时,size会自动-1。
在使用ArrayList的无参构造方法时,elementData会被赋值为 「DEFAULTCAPACITY_EMPTY_ELEMENTDATA」,此时是一个空数组,这就意味着只要向ArrayList中添加元素,它就需要进行[[#扩容]],此时,会重新创建一个大小为10的新数组。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
扩容
对于ArrayList的扩容,分为两种场景:
通过无参构造函数创建后,第一次添加元素
已有元素的扩容
创建后第一次添加
创建后第一添加元素,此时会创建一个大小为10的新数组。
已有元素扩容
计算现有数组的大小length,并在此基础上增加50%,也就是 3/2*length
作为newCapacity与minCapacity比较,两者相减为负,则newCapacity取minCapacity的值。
然后再用newCapacity与MAX_ARRAY_SIZE比较,重新结算newCapacity的值。
newCapacity的最大值为Integer.MAX_VALUE。
MAX_ARRAY_SIZE=Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
这里有个很有意思的点就是重点看 int newCapacity = oldCapacity + (oldCapacity >> 1)
,此时的newCapacity是通过相加计算出来的,它会存在「越界」的情况,也就是说newCapacity本身就可能是负值。
性能
对于ArrayLIst来讲,如果是使用index查元素的话,时间复杂度就是O(1)。如果使用查元素在ArrayList中的索引位置,时间复杂度就是O(n)。
遍历
for遍历
for (int i = 0; i < arr.size(); i++) {
Object item = arr[i];
}
for倒叙遍历
for (int i = arr.size() - 1; i >= 0; i--) {
Object item = arr[i];
}
for each
for (Object item : arr) {
// ...
}
Iterator迭代器
Iterator it = arr.iterator();
while (it.hasNext()) {
// it.next();
}
forEach,1.8支持
arr.forEach(item -> {
// ...
});
遍历中删除
「不可以使用正序遍历删除」,如果使用ArrayList的for正序遍历,在删除元素的过程中,会产生数据移位,导致删除不全的问题。
多线程
ArrayList在多线程中的线程不安全主要是在多线程中对ArrayList进行修改的过程中 size++
和 size--
是非原子性的,并且不具备内存可见性,同时ArrayList中没有锁的存在。