上篇我们学习了Collection
的相关源码,下面我们将继续学习List 家族中最常用的一个集合ArrayList
。
我们将从以下几个方面剖析ArrayList
。
ArrayList
的简介ArrayList
的数据结构ArrayList
的扩容机制ArrayList
的遍历
注意事项
全文对ArrayList
的源码解析均是基于JDK1.8
ArrayList
的简介
ArrayList
的定义
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
ArrayList
的类图如下:
从上述定义和类图中我们可以得到以下几点信息:
ArrayList
是一个集合,其继承了AbstractList
,实现了List
接口,有集合的基本操作,如添加,查找,删除。ArrayList
实现了Serializable
接口,说明其可以被序列化,可以序列化之后进行传输。ArrayList
实现了RandomAccess
接口,说明其可以被随机访问。ArrayList
实现了Cloneable
接口,说明其可以被克隆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