1. 为什么ArrayList增删效率低
首先,modCount是ArrayList的父类AbstractList中的变量,记录的是关于元素的数目被修改的次数,默认值为0。
增与删操作时, modCount一定会进行修改。改和查时modCount一定不会修改。
ArrayList底层是数组,查找时间复杂度为 O(1) ,删除/添加时间复杂度为 O(n)。(而链表查找时间复杂度为O(n), 删除/添加时间复杂度为O(1))
扩容操作会导致数组复制,批量删除会有找出两个集合交集的操作及数组复制操作,因此,增、删都相对低效。 而 改、查比较高效。
2. ArrayList扩容机制
总的来说就是分两步:
1、扩容
把原来的数组复制到另一个内存空间更大的数组中
2、添加元素
把新元素添加到扩容以后的数组中
1. 在无参构造中,我们看到了在用无参构造来创建对象的时候其实就是创建了一个空数组,长度为0
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // 0
}
2. 在有参构造中,传入的参数是正整数就按照传入的参数来确定创建数组的大小,否则异常
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);
}
}
扩容,即 add(E e)方法:
第一步:增加长度(关键点)
第二步:添加元素到数组
如果在添加的时候原数组是空的,就直接给一个10的长度,否则的话就 +1;
当需要的长度大于原来数组长度的时候就需要调用grow()方法进行扩容了,相反则不需要扩容
//判断当前ArrayList是否需要进行扩容
private void ensureExplicitCapacity(int minCapacity) {
//快速报错机制
modCount++;
// 如果修改后的数组容量大于当前的数组长度那么就需要调用grow进行扩容,反之则不需要
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//ArrayList扩容的核心方法,此方法用来决定扩充的容量
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 右移一位相当于除以2
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;
}
// 数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8字节。
// 2^31 -1 == 2,147,483,647。数组需要8 bytes去存储它自己的大小(2,147,483,647)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
hugeCapacity(int num)方法:
当扩容量(newCapacity)大于ArrayList数组定义的最大值后会调用hugeCapacity来进行判断。
如果minCapacity已经大于Integer的最大值(溢出为负数)那么抛出OutOfMemoryError(内存溢出)。
否则的话根据与MAX_ARRAY_SIZE的比较情况确定是返回Integer最大值还是MAX_ARRAY_SIZE。
这边也可以看到ArrayList允许的最大容量就是Integer的最大值(-2^31~2^31-1)。
Arrays.copyOf 方法底层调用的是 System.arraycopy
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Object src: 源数组, int srcPos: 从源数据的起始位置开始
Object dest: 目标数组, int destPos: 目标数组的开始起始位置
int length: 要copy的数组的长度
两者区别:
Arrays.copyOf() 不仅仅只是拷贝数组中的元素,在拷贝元素时,会创建一个新的数组对象
System.arraycopy() 只拷贝已经存在数组元素。
Vector每次扩容容量是翻倍,即为原来的2倍,而ArrayList是1.5倍