上篇我们学习了Collection的相关源码,下面我们将继续学习List 家族中最常用的一个集合ArrayList。
我们将从以下几个方面剖析ArrayList。
1.ArrayList的简介
2.ArrayList的数据结构
3.ArrayList的扩容机制
4.ArrayList的遍历
注意事项
全文对ArrayList的源码解析均是基于JDK1.8
ArrayList的简介
ArrayList的定义
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
ArrayList的类图如下:
从上述定义和类图中我们可以得到以下几点信息:
1.ArrayList是一个集合,其继承了AbstractList,实现了List 接口,有集合的基本操作,如添加,查找,删除。
2.ArrayList实现了Serializable接口,说明其可以被序列化,可以序列化之后进行传输。
3.ArrayList实现了RandomAccess接口,说明其可以被随机访问。
4.ArrayList实现了Cloneable接口,说明其可以被克隆
5.ArrayList支持泛型。
ArrayList的数据结构
看到本结我们心中不免有几个疑问:例如,
1. ArrayList到底是一个怎样的数据结构呢?
2. ArrayList是如何实现自动扩容的呢?
带着疑问我们来从源码中寻找答案:
//初始大小 private static final int DEFAULT_CAPACITY = 10; //初始数组 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. * 简译:没有指定初始大小的`ArrayList`再添加第一个元素之后初始大小变成10。 */ transient Object[] elementData; // non-private to simplify nested class access //构造指定初始容量的集合 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; } //将一个继承自`Collection`的集合转化成`ArrayList` public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this.elementData = EMPTY_ELEMENTDATA; } }
小结:从上述源码中可以看出,ArrayList是一个数组结构,所以其拥有数组的所有特性。
默认构造成Object[],如果传入泛型则构造成对应泛型的数组。
ArrayList的扩容机制
接着我们在看看ArrayList是如何实现自动扩容的。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!!(确保当前ArrayList维护的数组具有存储新元素的能力,经过处理之后将元素存储在数组elementData的尾部) elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 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); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
本部分源码的调用结构是
add--调用->ensureCapacityInternal--调用->ensureExplicitCapacity--调用-->grow--调用-->hugeCapacity
从中我们可知
1. ArrayList的极限大小是2*32 -1。(存储元素的个数为整型的范围。)
2. 当ArrayList扩容时首先会设计新的存储能力为 原来的1.5倍。
int newCapacity = oldCapacity + (oldCapacity >> 1);
确定ArrayList扩容之后最新的可存储元素之后,调用
elementData = Arrays.copyOf(elementData, newCapacity);
进行元素的元素的复制。
扩展:
public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); } public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
arraycopy标识为native意味JDK的本地库,不可避免的会进行IO操作,如果频繁的对ArrayList进行扩容,毫不疑问会降低ArrayList的使用性能,因此当我们确定添加元素的个数的时候,我们可以事先知道并指定ArrayList的可存储元素的个数,这样当我们向ArrayList中加入元素的时候,就可以避免ArrayList的自动扩容,从而提高ArrayList的性能。
ArrayList的遍历
ArrayList的遍历的遍历主要有三种方式:
package com.jay.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Created by xiang.wei on 2018/3/4 * * @author xiang.wei */ public class ArrayListIterTest { public static void main(String[] args) { //待遍历的ArrayList List<Integer> testList = new ArrayList<>(); for (int i = 0; i < 500000; i++) { testList.add(i); } testFori(testList); testFor(testList); testIter(testList); } private static void testFori(List<Integer> list) { long startTime = System.currentTimeMillis(); for (int i = 0; i < list.size(); i++) { list.get(i); } System.out.println("RandomAccess用时" + (System.currentTimeMillis() - startTime) + "ms"); } private static void testFor(List<Integer> list) { long startTime = System.currentTimeMillis(); for (Integer integer : list) { ; } System.out.println("for迭代用时" + (System.currentTimeMillis() - startTime) + "ms"); } private static void testIter(List<Integer> list) { long startTime = System.currentTimeMillis(); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { iterator.next(); } // for(Iterator<Integer> it = list.iterator(); it.hasNext(); ) { // it.next(); // } System.out.println("iterator迭代器用时" + (System.currentTimeMillis() - startTime) + "ms"); } }
我运行了5次,结果如下:
实验次数 | RandomAccess(ms) | For(ms) | Iterator(ms) |
第一次 | 10 | 21 | 9 |
第二次 | 10 | 16 | 17 |
第三次 | 10 | 19 | 11 |
第四次 | 11 | 18 | 18 |
第五次 | 13 | 21 | 12 |
平均数 | 10.8 | 19 | 13.4 |
从上述表格,我们可以看出RandomAccess(随机索引)访问的效率最高,For迭代的效率最低。
如果疑问,欢迎讨论。
相关的引用:
Java ArrayList的自动扩容机制
http://blog.csdn.net/yang1464657625/article/details/59109133