一直非常想写这一篇解读,奈何自己水平不高,看了很多资料,才了解清楚,本篇文章主要是对比1.7和1.8在添加元素时的不同,对删除,更新不做详细介绍,文章最后会给出两篇优秀的文章来详细介绍jdk1.8。
JDK1.8解读
先来看一下ArrayList的继承关系图谱
通过图谱可以观察出来ArrayList是继承了AbstractList,进一步对AarryList解读:可以看到ArrayList实现了List、RandomAccess 、Cloneable 、Serializable接口等。
实现List:说明ArrayList是存取有序,可以有重复元素,且元素可以是为null值;
实现RandomAccess:说明支持随机快速访问,但是该接口内并没定义任何逻辑,ArrayList底层数据结构是数组,所以可以快速访问;
实现Cloneable:说明支持拷贝,且是浅拷贝;
实现Serializable:说明可以实现序列化。
全局变量
构造函数
对构造函数进一步解读:
//默认构造函数
public ArrayList() {
//创建空列表,并不像1.7创建对象就实例化,更像一个懒加载
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//指定初始容量参数的构造函数
public ArrayList(int initialCapacity) {
// 容量大于0
if (initialCapacity > 0) {
//创建指定大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//创建容量为10空数组,
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 报错
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
}
}
//构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
public ArrayList(Collection<? extends E> coll) {
elementData = coll.toArray();
//如果指定集合元素个数不为0
if ((size = elementData.length) != 0) {
// coll.toArray 可能返回的不是Object类型的数组所以加上下面的语句用于判断,
//使用反射里面的getClass()得到elementData 所在类
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 用空数组代替
this.elementData = EMPTY_ELEMENTDATA;
}
}增加元素到ArrayList
添加元素,调用ensureCapacityInternal方法
ensureCapacityInternal调用ensureExplicitCapacity,再调用grow方法
// private int size;私有化一个int型变量
// 判断是否添加元素
public boolean add(E e) {
// 判断容量
ensureCapacityInternal(size + 1);
// 数组添加元素,size是下标
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
// 检查索引
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
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));
}
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private static int calculateCapacity(Object[] elementData, int 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) {
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;
// 新的数组长度等于原先数组长度加上原先数组长度的一半,oldCapacity右移一位相当于1/2 oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新数组长度减去数组元素个数小于0,则把数组元素个数作为新数组的长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新数组长度减去数组最大的定义长度,则调用hugeCapacity方法
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 元素数据等于,数组复制元素以及容量
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 小于0抛出异常
if (minCapacity < 0)
throw new OutOfMemoryError();
// 如果元素大于数组最大长度,返回整数的最大值,不然返回数组最大长度
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
在没有设置数组初始大小时,默认数组大小为0,例如:
ArrayList list =new ArrayList();//创建长度为object[] elementData初始化为{},并不是长度为10
list.add(1);//当第一次add时候,创建10长度的数组。向数组中添加元素 elementData[0] = new Integer(1)
....
list.add(11);//本次添加,数组容量不够,触发扩容机制,扩容后的数组长度为原来数组的1.5倍,并将原来数组中的元素复制到新数组中
添加元素的思路:
首先判断新增一个元素是否会超过数组大小,如果数组是一个空数组,添加元素就初始化一个长度为10 的数组;再次添加,直到数组溢出,溢出就扩容,每一次扩容是原来数组长度的1.5倍;最后使用Arrays.copyOf()方法将原来数组中的元素复制到新的数组中(此处为浅拷贝)。
推荐开发中尽量使用带参构造器,减少触发扩容机制,提高效率。
JDK1.7解读
看一下ArrayList的继承关系图谱
构造函数
添加元素的思路:
首先先创建一个长度为10的数组,接着判断新增一个元素是否会超过数组大小,如果不超过就直接添加数组长度缩小;再次添加,直到数组溢出,溢出就扩容,每一次扩容是原来数组长度的1.5倍;最后使用Arrays.copyOf()方法将原来数组中的元素复制到新的数组中(此处为浅拷贝)。
总结:jdk 1.7 中ArrayList的对象创建类似于单列的饿汉模式,一来就先创建好一个大小为10的数组,而jdk1.8中ArrayList的对象创建类似于单列的懒汉模式,只有添加数据才创建大小为10的数组,延迟了数组的创建,节省内存空间。