1、前言
对于平常开发的时候遇到的ArrayList,在此做一个简单的源码阅读记录,JDK1.8版本。
2、ArrayList的类关系
首先看下ArrayList的类关系图,可以看到实现了
- Serializable接口,支持序列化与反序列化;
- Cloneable接口,可以被克隆;
- RandomAccess接口,支持随机访问,另外fori循环会比迭代器循环效率高,代码如下:
for (int i=0, n=list.size(); i < n; i++) list.get(i);
比这个循环运行得更快:
for (Iterator i=list.iterator(); i.hasNext(); ) i.next();
3、ArrayList的源码
一、类的属性
/*** 默认初始容量 */private static final int DEFAULT_CAPACITY = 10;/** * (用于空实例的)共享空数组实例 */private static final Object[] EMPTY_ELEMENTDATA = {};/** * (用于默认大小的空实例的)共享空数组实例,我们将其与空的元素数据区分开来, * 以了解何时添加第一个元素. */private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/** * 存储ArrayList元素的数组缓冲区。ArrayList的容量是此数组缓冲区的长度。任何 * elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA elementData * 将在添加第一个元素时扩展到默认容量(10)。 */transient Object[] elementData;// transient关键字标记的成员变量不参与序列化过程/** * ArrayList的大小(包含的元素数) */private int size;/** * 可分配的数组的最大大小,可能会导致OutOfMemory错误:Requested array size exceeds VM limit */private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
二、add()方法
// 默认往数组末尾添加元素public boolean add(E e) { ensureCapacityInternal(size + 1); // 大小加1 elementData[size++] = e; return true;}private void ensureCapacityInternal(int minCapacity) {// minCapacity = size + 1 // elementData 是成员变量 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {/ 如果 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 数组为空时,返回较大的数 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; // 数组非空时,返回 minCapacity = size + 1}private void ensureExplicitCapacity(int minCapacity) {// minCapacity = size + 1 modCount++;// 计数 // 数组元素个数加1之后如果大于当前数组长度,则进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity);}private void grow(int minCapacity) {// minCapacity = size + 1 int oldCapacity = elementData.length;// 旧数组的长度 int newCapacity = oldCapacity + (oldCapacity >> 1);// 新数组的长度 = 1.5倍旧数组长度 // 新数组长度小于数组元素个数加1 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 新数组长度大于数组最大值 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 创建一个新的数组,并把旧数组元素复制过去,newCapacity为新数组大小 elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // 新数组长度大于数组最大值,并且minCapacity<0才会抛出oom错误 throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}// 指定位置添加元素public void add(int index, E element) { // 下标合法性校验 rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // 生成一个index位置为null的新数组 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++;}private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}
三、get()方法
public E get(int index) { // 检查合法性 rangeCheck(index); // 返回数组元素 return elementData(index);}private void rangeCheck(int index) { // 下标位置大于等于数组长度的时候(数组下标从0开始),抛出下标越界异常 if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}E elementData(int index) { return (E) elementData[index];}
四、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;}// 删除指定的元素,第一个出现的public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false;}private void fastRemove(int index) { modCount++; 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}五、set()方法
五、set()方法
// 将index位置的元素置换成elementpublic E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue;}
4、总结
- 通过阅读源码可以看出,ArrayList的这些方法都没有加锁,所以在多线程环境下是不安全的;
- add()和remove()方法都需要创建新的数组,是比较消耗性能的;