前言
面试中经常会被问到ArrayList。
经常看面试题的都知道这么回答:
ArrayList是一个有序集合,底层是数组实现的,默认是10,每次增长50%,并且线程不安全,查询速度快,插入速度慢
源码解析
下面来剖析一下集合里面的ArrayList
ArrayList是在JRE里面的rt.jar的java.util包中
根据源码,我们可以发现它继承自抽象类AbstractList,实现了List, RandomAccess, Cloneable, Serializable这些接口,并且通过通配符E来表述了它支持泛型。
AbstractList抽象类提供了add增加、remove删除、indexOf、lastIndexOf、clear、addAll等方法,并重写了equals和hashCode方法
初始化、初始容量和最大容量
ArrayList包含了两个重要的对象:elementData 和 size。
(01) elementData 是”Object[]类型的数组”,它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。
(02) size 则是动态数组的实际大小。
我们看源码
//默认的容量
private static final int DEFAULT_CAPACITY = 10;
//新new出来不指定大小的ArrayList elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA
transient Object[] elementData;
private int size; //ArrayList的size
//构造方法,带参数:
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);
}
}
继续往下读,发现有个trimToSize方法
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
trimToSize是可以用该方法来修整此ArrayList实例的是列表的当前大小的容量,就是List里面有多少个值,容量就是多少
ArrayList默认不带参数的容量是10,假如new的时候指定了容量,那么直接就是那个容量,那么最大的容量是多少,根据源码可知MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,即2^31-1,就是2的31次方的值再减1
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
add与容量增长
那么ArrayList的add是如何增长的呢?
看下面
元素被add的时候方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
调用ensureCapacityInternal,ensureCapacityInternal调用ensureExplicitCapacity,通过判断if (minCapacity - elementData.length > 0)来调用grow增长一次,grow方法是私有的,增长的关键在于int newCapacity = oldCapacity + (oldCapacity >> 1);
(oldCapacity >> 1)表示oldCapacity 向右移动一位,也就是除以2
所以是增长一半
增长完毕以后再elementData = Arrays.copyOf(elementData, newCapacity)复制元素到elementData中
>>是表示位运算符,就是往右移动1位,如果num >> n,表示num除以2的n次方
<<是表示向左移动,如果num << n,表示num乘以2的n次方
ArrayList的克隆
ArrayList的克隆是讲元素用Arrays.copyOf复制到新的对象
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
remove删除
并且也有内部私有化的batchRemove方法,批量删除,主要用于retainAll中
retainAll:保留指定集合,剩下的全部删除