18.2.3 ArrayList vs LinkedList性能对比
1. 数据结构基础
1.1 ArrayList底层实现
ArrayList基于动态数组实现,内部使用Object[]数组存储元素。
public class ArrayList<E> extends AbstractList<E> implements List<E> {
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 存储元素的数组
transient Object[] elementData;
// 实际元素数量
private int size;
// 构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
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);
}
}
}
1.2 LinkedList底层实现
LinkedList基于双向链表实现,每个节点包含数据和前后指针。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E> {
// 链表大小
transient int size = 0;
// 头节点
transient Node<E> first;
// 尾节点
transient Node<E> last;
// 节点结构
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// 构造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
}
2. 核心操作性能分析
2.1 随机访问性能对比
ArrayList随机访问
public class ArrayListRandomAccess {
// ArrayList的get方法 - O(1)
public E get(int index) {
rangeCheck(index); // 边界检查
return elementData(index); // 直接数组访问
}
E elementData(int index) {
return (E) elementData[index];
}
// 性能测试
public void testRandomAccess() {
ArrayList<Integer> arrayList = new ArrayList<>();
// 添加10万个元素
for (int i = 0; i < 100000; i++) {
arrayList.add(i);
}
long startTime = System.nanoTime();
// 随机访问1万次
Random random = new Random();
for (int i = 0; i < 10000; i++) {
int index = random.nextInt(arrayList.size());
Integer value = arrayList.get(index);
}
long endTime = System.nanoTime();
System.out.println("ArrayList随机访问耗时: " + (endTime - startTime) / 1000000 + "ms");
}
}
LinkedList随机访问
public class LinkedListRandomAccess {
// LinkedList的get方法 - O(n)
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
// 根据索引查找节点
Node<E> node(int index) {
// 优化:从距离较近的一端开始查找
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
// 性能测试
public void testRandomAccess() {
LinkedList<Integer> linkedList = new LinkedList<>();
// 添加10万个元素
for (int i = 0; i < 100000; i++) {
linkedList.add(i);
}
long startTime = System.nanoTime();
// 随机访问1万次
Random random = new Random();
for (int i = 0; i < 10000; i++) {
int index = random.nextInt(linkedList.size());
Integer value = linkedList.get(index);
}
long endTime = System.nanoTime();
System.out.println("LinkedList随机访问耗时: " + (endTime - startTime) / 1000000 + "ms");
}
}
2.2 插入操作性能对比
ArrayList插入操作
public class ArrayListInsert {
// 尾部插入 - O(1) 均摊时间复杂度
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保容量
elementData[size++] = e;
return true;
}
// 指定位置插入 - O(n)
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 ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 性能测试
public void testInsertPerformance() {
ArrayList<Integer> list = new ArrayList<>();
// 测试尾部插入
long startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
long endTime = System.nanoTime();
System.out.println("ArrayList尾部插入耗时: " + (endTime - startTime) / 1000000 + "ms");
// 测试头部插入
list.clear();
startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
list.add(0, i); // 头部插入
}
endTime = System.nanoTime();
System.out.println("ArrayList头部插入耗时: " + (endTime - startTime) / 1000000 + "ms");
}
}
LinkedList插入操作
public class LinkedListInsert {
// 尾部插入 - O(1)
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
// 指定位置插入 - O(n)
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
// 头部插入 - O(1)
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
// 性能测试
public void testInsertPerformance() {
LinkedList<Integer> list = new LinkedList<>();
// 测试尾部插入
long startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
long endTime = System.nanoTime();
System.out.println("LinkedList尾部插入耗时: " + (endTime - startTime) / 1000000 + "ms");
// 测试头部插入
list.clear();
startTime = System.nanoTime();
for (int i = 0; i < 100000; i++) {
list.addFirst(i); // 头部插入
}
endTime = System.nanoTime();
System.out.println("LinkedList头部插入耗时: " + (endTime - startTime) / 1000000 + "ms");
}
}
2.3 删除操作性能对比
ArrayList删除操作
public class ArrayListRemove {
// 根据索引删除 - O(n)
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // 清除引用
return oldValue;
}
// 根据对象删除 - O(n)
public boolean remove(Object o) {
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;
}
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;
}
// 性能测试
public void testRemovePerformance() {
ArrayList<Integer> list = new ArrayList<>();
// 准备数据
for (int i = 0; i < 100000; i++) {
list.add(i);
}
// 测试头部删除
long startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
if (!list.isEmpty()) {
list.remove(0); // 头部删除
}
}
long endTime = System.nanoTime();
System.out.println("ArrayList头部删除耗时: " + (endTime - startTime) / 1000000 + "ms");
}
}
LinkedList删除操作
public class LinkedListRemove {
// 根据索引删除 - O(n)
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
// 删除头部元素 - O(1)
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null;
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
// 删除节点
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
// 性能测试
public void testRemovePerformance() {
LinkedList<Integer> list = new LinkedList<>();
// 准备数据
for (int i = 0; i < 100000; i++) {
list.add(i);
}
// 测试头部删除
long startTime = System.nanoTime();
for (int i = 0; i < 10000; i++) {
if (!list.isEmpty()) {
list.removeFirst(); // 头部删除
}
}
long endTime = System.nanoTime();
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
查看10道真题和解析