ArrayList 是 Java 中非常常用的数据结构,其底层基于 Object[] 数组实现,其特点是:基于 Object[] 数组,查询快,可随机访问,可以动态增加容量,增加和删除慢,线程不安全。
ArrayList 是线程不安全的,在多线程环境下推荐使用 CopyOnWriteArrayList 或者 Vector。
下图显示的 ArrayList 的继承关系图:
- 实现 Cloneable 接口,覆写其 clone() 方法,实现数组元素的克隆;
- 实现 Serializable 接口,表示这可以系列化传输;
- 实现 RandomAccess 接口,表示实现这个接口的 List 支持随机访问,也就是通过 index 序号快速获取元素;
- 继承 AbstractList ,AbstractList 是一个数组队列,提供了添加、删除、修改、遍历等功能;
1、ArrayList 核心代码
1.1、ArrayList 的构造方法
/** 默认初始容量 */
private static final int DEFAULT_CAPACITY = 10;
/** 默认的空数组实例,用于初始化容量为零的数组 */
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默认的空数组实例,用于初始化容量为零的数组
* 但插入第一条数据的时候会判断是否等于这个数组,如果是,直接扩展为默认容量
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/** 存储实际的数据的数组 */
transient Object[] elementData; // non-private to simplify nested class access
/** 所包含的元素个数 */
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);
}
}
/**
* 无参构造函数,初始化为一个空数组
* 但插入第一个数据的时候,如果 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA 初始化为默认容量为 10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造包含指定集合的元素的的列表
*/
public ArrayList(Collection<!--? extends E--> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray 可能不是返回 Object[] 类型
if (elementData.getClass() != Object[].class) {
// 将原来不是 Object[]类型的数组转换为 Object[]
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
} else {
// 长度等于 0 用空数组代替
this.elementData = EMPTY_ELEMENTDATA;
}
}
- 上面的无参构造函数,实际上会初始化一个空的数组,当对数组进行增加操作时候,才会真正将数组扩容为为默认 10 。也就是说,当向数组添加第一个元素的时候,才会将数组的容量扩充为 10。
1.2、Array 扩容机制
> 这里以 JDK 11 为例,JDK 1.8 和 JDK 11 关于扩容的代码差异比较大,但其核心的逻辑结构是一样的。
- 在调用 ArrayList 增加元素的方法时,如果数组当前的大小等于数组的容量,则调用 grow() 方法扩容
private void add(E e, Object[] elementData, int s) {
// 如果数组的大小已经等于容量了,就使用 grow 方法扩容
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
// 往数组的末尾增加一个元素
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
- grow() 方法:
private Object[] grow(int minCapacity) {
// newCapacity(minCapacity) 计算数组的最小扩容容量
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private Object[] grow() {
return grow(size + 1);
}
- newCapacity(minCapacity)
private int newCapacity(int minCapacity) {
// overflow-conscious code
// 旧的容量
int oldCapacity = elementData.length;
// 计算新数组的容量,约等于旧数组的 1.5 倍
// >> 不带符号的右移运算符,比整除运算快,右移 1 位约为原来的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果 newCapacity 小于等于 minCapacity
if (newCapacity - minCapacity <= 0) {
// 如果是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// 这里也就是为什么无参构造函数增加增加一个数据后,数组的容量变成默认容量 10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// 1、无参构造函数函数构造的 ArrayList,size 为 0
// 2、使用 add 增加一个元素的时候,调用 grow 扩容
// 3、max(10,size + 1 = 1),所以第一次 add 增加一个元素的时候容量会为 10
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
// 如果 newCapacity 大于 minCapacity
return (newCapacity - MAX_ARRAY_SIZE <= 0)
// 如果 newCapacity 大于等于 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)
? newCapacity
// 比较 minCapacity 跟 MAX_ARRAY_SIZE 的大小
: hugeCapacity(minCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 比较 minCapacity 跟 MAX_ARRAY_SIZE 的大小
// 如果大于则 Integer.MAX_VALUE,否则 MAX_ARRAY_SIZE
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
1.2.1、ensureCapacity 方法
ensureCapacity 这个方法在 ArraryList 中没有被调用过,主要是在增加大量元素前,最好在初始化的时候指定容量或者先调用一下这个方法,减少 ArrayList 扩容的次数和数组 Arrays.copyOf() 拷贝的次数。
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
测试:
ArrayList<integer> list = new ArrayList<>();
int n = 10000000;
long start = System.currentTimeMillis();
// 注释或者去掉注释 ensureCapacity 分别运行代码
// 注释后 耗时:313
// 去掉注释 耗时:226
list.ensureCapacity(n);
for (int i = 0; i < n; i++) {
list.add(i);
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end - start));
扩容的核心逻辑:
- 如果 size 等于 elementData.length ,则进行扩容
- 无参构造函数的List 调用 add 增加一个元素的时候,其容量会扩展为默认容量大小,也就是 10
- 其他情况下,将 List 扩容为先前的约 1.5 倍。
- 假设现在的 ArrayList<>(10),也就是容量为 10,增加第 1、2、3....9、10 个元素的时候不会扩容,增加第十一个元素的时候,会扩容我原来的约 1.5倍,也就是容量为 15。
**ArrayList 源码 JDK 1.8 **:
> JDK 11 的改动比较大,主要是扩容方面的代码,上面有分析
public class ArrayList<e> extends AbstractList<e>
implements List<e>, RandomAccess, Cloneable, java.io.Serializable {
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; // non-private to simplify nested class access
/**
* 所包含的元素个数
*/
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);
}
}
/**
* 无参构造函数,初始化为一个空数组
* 但插入第一个数据的时候,如果 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA 初始化为默认容量为 10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造包含指定集合的元素的的列表
*/
public ArrayList(Collection<!--? extends E--> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray 可能不是返回 Object[] 类型
if (elementData.getClass() != Object[].class) {
// 将原来不是 Object[]类型的数组转换为 Object[]
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
} else {
// 长度等于 0 用空数组代替
this.elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 去除数组中多余的空间,将数组的容量跟大小一样
* 如果数组大小为 0 则是空数组
*/
public void trimToSize() {
// 记录修改次数
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
/**
* 确保有足够的容量来容纳元素
* minCapacity 当前数组所需的最小容量
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
/**
* 计算当前最小需要扩容容量,
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA 午餐构造函数构造成这个
// 所以说使用这个构造方法的数组添加第一个元素后,数组的容量变成 DEFAULT_CAPACITY = 10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 再 add 等增加元素的方法中,调用这个方法判断数组是否有足够的容量容纳数据
*/
private void ensureCapacityInternal(int minCapacity) {
// calculateCapacity 计算数组所需的最小容量
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 如果当前数组容量小于最小容量,对数组进行扩容
* @param minCapacity 最小所需的容量
*/
private void ensureExplicitCapacity(int minCapacity) {
// 修改次数 +1
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 增加数组的容量,扩容
grow(minCapacity);
}
/**
* 最大的数组容量 2 的 31 次方 - 9
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* ArrayList 扩容的核心代码
*/
private void grow(int minCapacity) {
// 获取旧数组的容量
int oldCapacity = elementData.length;
// 计算新数组的容量,约等于旧数组的 1.5 倍
// >> 带符号的右移运算符,比整除运算快,右移 1 位约为原来的一半
// 偶数右移 1 位等于 x / 2
// 奇数右移 1 位等于 (x -1) / 2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果数组新的容量小于最小所需的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果数组的新容量大于 MAX_ARRAY_SIZE
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 比较 minCapacity 跟 MAX_ARRAY_SIZE 的大小
// 如果大于则 Integer.MAX_VALUE,否则 MAX_ARRAY_SIZE
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 比较 minCapacity 跟 MAX_ARRAY_SIZE 的大小
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) { // overflow
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 返回当前数组的大小,也就是包含多少个数组
*/
public int size() {
return size;
}
/**
* 如果当前数组的大小为 0 ,则返回 true
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 返回数组中是否包含某个元素
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* 如果数组包含某个元素,则返回其索引,否则返回 -1
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i] == null)
return i;
} else { // 循环遍历,然后使用 equals 比较
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/**
* 返回指定元素在数组中最大的索引值,没有该元素就返回 -1
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size - 1; i >= 0; i--)
if (elementData[i] == null)
return i;
} else {
for (int i = size - 1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
/**
* ArrayList 的克隆方法
*/
public Object clone() {
try {
// 浅拷贝,元素数组并不会拷贝
ArrayList<!--?--> v = (ArrayList<!--?-->) super.clone();
// 拷贝数组
v.elementData = Arrays.copyOf(elementData, size);
// 修改次数为 0
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// 这不应该发生的,因为 Array 实现了 Cloneable
throw new InternalError(e);
}
}
/**
* 将数组拷贝一份,返回(数组中元素的顺序不会变化)
* 返回并不是 this.elementData 的引用,而是其拷贝
* 因此在修改返回的数组不会影响 this.elementData 的数据
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
/**
* 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素)
*/
@SuppressWarnings("unchecked")
public <t> T[] toArray(T[] a) {
// 如果 a 的容量小于 this.elementData 的大小
if (a.length < size)
// 将 ArrayList 的内容拷贝一份,然后使用 T[] 类型返回
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// a 的容量大于或等于 this.elementData 的大小
// 使用 System.arraycopy() 将 elementData 的内容拷贝给 a
// System.arraycopy 实现数组之间的拷贝,Arrays.copyOf 依赖于此方法
System.arraycopy(elementData, 0, a, 0, size);
// 如果 a 的容量大于 size,则 a[size] = null
// 只有在调用者直到数组中不包含 null 的时候才能够确定其长度,
if (a.length > size)
a[size] = null;
return a;
}
/**
* 返回指定 index 的元素
*/
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
/**
* 返回指定 index 的元素
*/
public E get(int index) {
// 检查 index 是否符合规范
rangeCheck(index);
return elementData(index);
}
/**
* 替换指定位置的元素
*/
public E set(int index, E element) {
// 检查 index
rangeCheck(index);
// 获取旧的元素
E oldValue = elementData(index);
// 替代旧的元素
elementData[index] = element;
return oldValue;
}
/**
* 再数组最后增加一个元素
*/
public boolean add(E e) {
// 计算是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 在指定位置插入元素
*/
public void add(int index, E element) {
// 检查 index
rangeCheckForAdd(index);
// 计算是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 使用 System.arraycopy 将 index 及其之后的元素后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// index 设置为指定的元素
elementData[index] = element;
size++;
}
/**
* 移除指定 index 的元素
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
// 旧的元素
E oldValue = elementData(index);
// numMoved 等于 0 表示最后一个元素
int numMoved = size - index - 1;
if (numMoved > 0)
// index 之后的元素前移一位
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 移除指定元素,第一个出现的,返回 true 表示包含指定的元素,并成功移除了第一个
*/
public boolean remove(Object o) {
// 如果指定元素等于 null
if (o == null) {
// 遍历,
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/**
* 移除指定 index 的元素
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
/**
* 情况数组中所有的元素
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
// 大小变成 0
size = 0;
}
/**
* 将集合的元素添加到 elementData 的末尾
*/
public boolean addAll(Collection<!--? extends E--> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
/**
* 将集合的元素添加到指定 index 之后
*/
public boolean addAll(int index, Collection<!--? extends E--> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
// 如果要移动原数组,就像原数组后移
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 将指定集合 a 的元素复制到 elementData 的 index 之后
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
/**
* 删除指定范围 [fromIndex,toIndex) 的数据
*/
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex - fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
/**
* 检查 index 是否符合要求
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 也是检查 index 是否符合范围,增加数据的时候使用
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 返回 IndexOutOfBoundsException 细节信息
*/
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
/**
* 从 elementData 删除指定集合中 c 交集的元素
*/
public boolean removeAll(Collection<!--?--> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 从 elementData 仅保留指定集合 c 中交集的元素
*/
public boolean retainAll(Collection<!--?--> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
/**
* 上面两个函数调用的如果修改了则返回 true
*/
private boolean batchRemove(Collection<!--?--> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
/**
* 系列化对象
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i = 0; i < size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* 反序列化对象
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i = 0; i < size; i++) {
a[i] = s.readObject();
}
}
}
/**
* 从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。
* 指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。
* 返回的列表迭代器是 fail-fast ,fail-safe 允许在遍历的过程中对容器中的数据进行修改,而fail-fast则不允许
* https://blog.csdn.net/chenssy/article/details/38151189
*/
public ListIterator<e> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: " + index);
return new ListItr(index);
}
/**
* 返回列表中的列表迭代器(按适当的顺序)。
* 返回的列表迭代器是fail-fast
*/
public ListIterator<e> listIterator() {
return new ListItr(0);
}
/**
* 以正确的顺序返回该列表中的元素的迭代器。
* 返回的迭代器是 fail-fast 。
*/
public Iterator<e> iterator() {
return new Itr();
}
}
</e></e></e></t></e></e></e></integer>
来源:oschina
链接:https://my.oschina.net/u/3274821/blog/4484643