当前位置 博文首页 > 氷泠:【2w字干货】ArrayList与LinkedList的区别以及JDK11中的底
本文主要讲述了ArrayList
与LinkedList
的相同以及不同之处,以及两者的底层实现(环境OpenJDK 11.0.12
)。
在详细介绍两者的底层实现之前,先来简单看一下两者的异同。
List
接口,都继承了AbstractList
(LinkedList
间接继承,ArrayList
直接继承)ArrayList
基于Object[]
数组,LinkedList
基于LinkedList.Node
双向链表ArrayList
随机访问能做到O(1)
(实现 RandomAccess
),因为可以直接通过下标找到元素,而LinkedList
需要从头指针开始遍历,时间O(n)
ArrayList
无参初始化时初始化为一个空数组,而LinkedList
不需要ArrayList
当长度不足以容纳新元素的时候,会进行扩容,而LinkedList
不会ArrayList
的内存空间占用主要体现在数组结尾会预留一定容量空间,而LinkedList
空间花费主要体现在每一个元素都需要消耗比ArrayList
更多的空间ArrayList
底层底层使用Object[]
数组实现,成员变量如下:
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
transient Object[] elementData;
private int size;
private static final int MAX_ARRAY_SIZE = 2147483639;
默认的初始化容量为10,接下来是两个空数组,供默认构造方法以及带初始化容量的构造方法使用:
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else {
if (initialCapacity != 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
this.elementData = EMPTY_ELEMENTDATA;
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
下面再来看一些重要方法,包括:
add()
remove()
indexOf()/lastIndexOf()/contains()
add()
add()
方法有四个:
add(E e)
add(int index,E e)
addAll(Collection<? extends E> c)
addAll(int index, Collection<? extends E> c
add()
先来看一下add(E e)
以及add(int index,E eelment)
:
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length) {
elementData = this.grow();
}
elementData[s] = e;
this.size = s + 1;
}
public boolean add(E e) {
++this.modCount;
this.add(e, this.elementData, this.size);
return true;
}
public void add(int index, E element) {
this.rangeCheckForAdd(index);
++this.modCount;
int s;
Object[] elementData;
if ((s = this.size) == (elementData = this.elementData).length) {
elementData = this.grow();
}
System.arraycopy(elementData, index, elementData, index + 1, s - index);
elementData[index] = element;
this.size = s + 1;
}
add(E e)
实际调用的是一个私有方法,判断是否需要扩容之后,直接添加到末尾。而add(int index,E element)
会首先检查下标是否合法,合法的话,再判断是否需要扩容,之后调用System.arraycopy
对数组进行复制,最后进行赋值并将长度加1。
关于System.arraycopy
,官方文档如下:
一共有5个参数:
也就是说:
System.arraycopy(elementData, index, elementData, index + 1, s - index);
的作用是将原数组在index
后面的元素“往后挪”,空出一个位置让index
进行插入。
addAll()
下面来看一下两个addAll()
:
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
++this.modCount;
int numNew = a.length;
if (numNew == 0) {
return false;
} else {
Object[] elementData;
int s;
if (numNew > (elementData = this.elementData).length - (s = this.size)) {
elementData = this.grow(s + numNew);
}
System.arraycopy(a, 0, elementData, s, numNew);
this.size = s + numNew;
return true;
}
}
public boolean addAll(int index, Collection<? extends E> c) {
this.rangeCheckForAdd(index);
Object[] a = c.toArray();
++this.modCount;
int numNew = a.length;
if (numNew == 0) {
return false;
} else {
Object[] elementData;
int s;
if (numNew > (elementData = this.elementData).length - (s = this.size)) {
elementData = this.grow(s + numNew);
}
int numMoved = s - index;
if (numMoved > 0) {
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
}
System.arraycopy(a, 0, elementData, index, numNew);
this.size = s + numNew;
return true;
}
}
在第一个addAll
中,首先判断是否需要扩容,接着也是直接调用目标集合添加到尾部。而在第二个addAll
中,由于多了一个下标参数,处理步骤稍微多了一点:
if
里面的System.arraycopy
index
位置上面的add()
方法都涉及到了扩容,也就是grow
方法,下面来看一下:
private Object[] grow(int minCapacity) {
return this.elementData = Arrays.copyOf(this.elementData, this.newCapacity(minCapacity));
}
private Object[] grow() {
return this.grow(this.size + 1);
}
private int newCapacity(int minCapacity) {
int oldCapacity = this.elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);
} else if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
return minCapacity;
}
} else {
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity