ArrayList源码解析
全局变量说明
-
// 默认容量 private static final int DEFAULT_CAPACITY = 10; // 空数组,用于初始化时当初始化大小为0 // 或者初始化时指定的集合大小为0时赋值给数组elementData private static final Object[] EMPTY_ELEMENTDATA = {}; // 默认空数组, private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 存储元素的数组 transient Object[] elementData; // 元素个数 private int size;
三种集合初始化方法
-
指定初始化容量
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { // 初始化list容量大于0时,创建大小为initialCapacity的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { // 初始化list容量等于0时,elementData为空数组 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
-
无参数直接进行初始化,elementData数组为默认空数组
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
-
指定初始化数据,参数为集合对象
public ArrayList(Collection<? extends E> c) { // 指定的集合转为数组 elementData = c.toArray(); if ((size = elementData.length) != 0) { // 由于java8及之前的版本存在的bug会导致c.toArray()返回的类型可能不为Object[], // 这样就无法为数组添加多种不同类型的数据,因此通过数组复制的方式 // 将其转化为Object[]. // java9已解决c.toArray()返回结果不为Object[].class的问题. // 链接:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652 if (elementData.getClass() != Object[].class) { elementData = Arrays.copyOf(elementData, size, Object[].class); } } else { // 指定的初始化数据大小为0时,elementData数组为空数组 this.elementData = EMPTY_ELEMENTDATA; } }
集合新增
-
public boolean add(E e) { // 确保数据容量足够 ensureCapacityInternal(size + 1); // 将当前元素添加到数组末尾 elementData[size++] = e; return true; }
-
// 计算存储数据所需要的最小容量 private static int calculateCapacity(Object[] elementData, int minCapacity) { // 当数组为空数组时,由于存在addAll方法,可能会一次添加了大量元素,超过默认容量10 // 因此所需最小容量取默认容量10和minCapacity的最大值 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } // 确保容量足够,若不够需要进行扩容 private void ensureExplicitCapacity(int minCapacity) { //每次数组发生变化时+1,用于快速失败 modCount++; // 如果所需容量大于当前数组容量,则进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); }
-
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 扩容 private void grow(int minCapacity) { // 当前数组容量 int oldCapacity = elementData.length; // 新的容量为原来数组容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容后如果容量还是不够,则新的容量为之前计算的所需要的最小容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 底层调用System.arraycopy进行元素的拷贝 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { // 当size+1的结果超过int所表示的最大值时,minCapacity会小于0 if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
相关问题解释
- 为什么要使用EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA两个空数组?
- 使用两个空数组是为了区分两种不用的初始化方式,使用同一个空数组也是完全没有问题的
- 变量elementData为什么要使用transient关键字?
- 被transient修饰的对象不会进行序列化,elementData为数组容器,容器可能未被填满,如果将整个elementData数组进行序列会消耗更多的资源,ArrayList通过内部定义的writeObject和readObject进行序列化和反序列化,只对集合中的元素进行操作。
- MAX_ARRAY_SIZE的作用?
- 避免一些虚拟机内存溢出,-8是为了减少出错的几率,数组最大的长度依然是Integer.MAX_VALUE