目录
1.重要属性和构造
ArrayList内部维护了一个数组,这个数组才是真正存储数据的容器
transient Object[] elementData;
当进行初始化new ArrayList<E>()时,会给elementData赋值一个空数组.
2.核心方法
add
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
里面就两句,第二句好理解,就是给elementData的尾巴添加一个元素.
第一句的作用则是检查扩容elementData,此时elementData.size = 0,深入查看一下.
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
还得深入一下,先看 calculateCapacity(空数组,1);
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//DEFAULT_CAPACITY默认为10,
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果不是空数组,则保持不变
return minCapacity;
}
calculateCapacity方法目的是为了获得现在elementData最少要有的长度,否则盛不下那么元素。在add方法里面让现有的容量加一传参过来,在calculateCapacity方法里基本上直接返回minCapacity,当然这里面也做了小的调整,说如果数组是空的,也就是第一次添加的时候,就让最少要有长度设置为10
接下来看ensureExplicitCapacity方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount实际上是指数组修改了多少次.这个这里没什么用.
下面两句意思是如果最少要有的长度比现在数组的长度还要大,也就是说现在这个elementData已经不能满足再添加一个元素了,容量不够了,就执行grow方法
接着看grow
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//关键在这里 新数组的长度 = 旧长度 + 旧长度/2
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
elementData = Arrays.copyOf(elementData, newCapacity);
}
结论:
新建ArrayList后,默认elementData为空数组。
第一次添加元素,由于ensureExplicitCapacity方法里的10-0>0,则会执行grow方法,将elementData扩容为长度为10的数组,然后往里面添加元素
第二次添加元素,由于ensureExplicitCapacity方法里的2-10<0,则不走grow,直接往elementData里面添加元素。
第三次添加元素。。。
。。。
。。。
第十次添加元素,由于ensureExplicitCapacity方法里的11-10>0,则会执行grow方法,将elementData扩容为长度为10+5 = 15的数组,然后往里面添加元素
。。。
。。。
第十五次添加元素,由于ensureExplicitCapacity方法里的16-15>0,则会执行grow方法,将elementData扩容为长度为15+7 = 22的数组,然后往里面添加元素
。。。
。。。
以此类推,直到数组容量达到Int的最大值-8。
由此可见,ArrayList里add方法是要不定时的copy数组,相当消耗资源
remove
只看下面这个方法,根据索引删除
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
其他都还好,如果你仔细揣摩的话,你就会了解,基本上所有的remove都会执行 System.arraycopy这个方法。本方法里这个arraycopy的意思是会将被删除的元素后面的所有元素都往前挪一位。效果相当于
for(){
//如果是最后的元素,将元素置为null
if (i == size){
arr[i] = null;
break;
}
//让元素往前挪一位
if (i >= remove_index){
arr[i] = arr[i+1];
}
}
其他的remove方法里面也这样System.arraycopy了。
结论:
ArrayList的remove方法均会操作“被删除索引”后的所有元素,非常消耗资源。
get
源码是这样的。非常简单。
E elementData(int index) {
return (E) elementData[index];
}
结论:
Arraylist的get方法非常简单高效
set
elementData[index] = element;
也基本上是这个操作。
结论:
ArrayList的set方法也是非常简单高效。
3.总结
ArrayList的get和set简单高效,add和remove非常耗资源,如果数组元素非常多的话,估计要炸。