简单介绍
ArrayList是Java通过动态数组来实现的一个集合类,可以用来存储有序集合。方便且高效,平时项目中用得非常多。
作为程序员的我们,对于这些经常用的工具,都想去了解其运行机制。所以这天,我好奇地new了一个ArrayList。通过调试,准备了解其内部实现。当然,既然是动态数组,重点自然是要去看看它的扩容机制的。我很快发现,原来没有那么难。菜鸡的我都能读得有来有回。那么下面就来记录一下,我读ArrayList的全部过程。😀
开干就完事
-
第一步自然是要new一个
List<Integer> list = new ArrayList,ctrl然后点一下。进入其内部。private static final long serialVersionUID = 8683452581122892189L; /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;我去,看到这一堆密密麻麻的英文属实是要把我劝退,没事,简单看下。
private static final long serialVersionUID = 8683452581122892189L; // 默认初始化容量 private static final int DEFAULT_CAPACITY = 10; // 共享的空数组,用于空实例 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) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }- 当初始容量大小设置为正数,数组就直接new就完事,数组大小就是输入容量大小
- 初始容量设置为零,数组就设为EMPTY_ELEMENTDATA
- 如果不设置初始容量,也就是默认大小嘛,数组设为DEFAULTCAPACITY_EMPTY_ELEMNTDATA。
-
居然是扩容,那么首先就先看一下add嘛,毕竟这里面肯定有扩容。
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }果然,在add在赋值前,通过ensureChapacityInternal来确保容量大小是足够的,ctrl点进去看看
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }看到这里,我没想到这么快就清晰了。在第一次add时,如果是通过new ArrayList(), 数组值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在add时,比较size+1和DEFAULT_CAPACITY(10),也就是1和10比较,最后minCapacity为10。 ctrl点进去ensureExplicitCapacity看看。
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }这里先检验是否需要扩容,然后调用grow进行扩容
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; 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 = Arrays.copyOf(elementData, newCapacity); }- 看到这里,newCapacity = oldCapacity + (oldCapacity >> 1),也就是1.5倍左右。如果newCapacity比minCapacity大,则容量扩充为newCapacity,否则容量扩充为minCapacity。
- 我们就知道了,每次扩容都是以1.5倍的速度进行扩容的。如果是通过new ArrayList()的方式创建List,那么在第一次add的时候,容量不是扩容到1,而是直接扩容为10
- 关于hugeCapacity(minCapacity),如果minCapacity大于最大容量,则新容量则为Integer.MAX_VALUE,否则为 MAX_ARRAY_SIZE
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }看到这里,我就有些疑惑了,MAX_ARRAY_SIZE不是最大容量了吗,为什么我们还有机会比这个容量更大。现在我还不是很理解。😴