面试者经常遇到集合类源码的问题。我们不求将所有的细节都记住,但ArrayList与LinkedList比较、add、get、remove、扩容、及相关时间复杂度等核心思想要说得一清二楚。
ArrayList底层用数组实现,可以快速访问某一节点的值,但插入删除会调用System.arraycopy方法,数组容量不够时需进行扩容,扩容采用新的大数组取代旧的数组。
public class MyArrayList<E> {
private Object[] elementData;
public int size;// 数组中元素长度
private static final int INITIAL_CAPACITY = 10;// 初始容量
public MyArrayList() {
elementData = new Object[INITIAL_CAPACITY];
}
public MyArrayList(int capacity) {
elementData = new Object[capacity];
}
private void ensureCapacity(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
int newCapacity = (oldCapacity * 3) / 2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);// 扩容调用Arrays.copyOf方法 将容量扩为原数组长度的3/2, 现有的数组引用指向了新的数组
// 时间复杂度为O(n)
}
}
// 在數組末尾添加元素 時間複雜度O(1)
public void add(E e) {
if (e == null)
return;
ensureCapacity(size + 1);
elementData[size++] = e;
}
public void add(E e, int index) {
if (e == null || index > size || index < 0)
return;
ensureCapacity(size + 1);
System.arraycopy(elementData, index, elementData, index + 1, size - index); // 数组中间添加元素
//平均时间复杂度O(n)
elementData[index] = e;
size++;
}
public E get(int index) {
if (index > size || index < 0)
return null;
return (E) elementData[index]; // 按位置获取元素 时间复杂度为O(1)public class MyArrayList<E> {
private Object[] elementData;
public int size;// 数组中元素长度
private static final int INITIAL_CAPACITY = 10;// 初始容量
public MyArrayList() {
elementData = new Object[INITIAL_CAPACITY];
}
public MyArrayList(int capacity) {
elementData = new Object[capacity];
}
private void ensureCapacity(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
int newCapacity = (oldCapacity * 3) / 2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);// 扩容调用Arrays.copyOf方法 将容量扩为原数组长度的3/2, 现有的数组引用指向了新的数组
// 时间复杂度为O(n)
}
}
// 在數組末尾添加元素 時間複雜度O(1)
public void add(E e) {
if (e == null)
return;
ensureCapacity(size + 1);
elementData[size++] = e;
}
public void add(E e, int index) {
if (e == null || index > size || index < 0)
return;
ensureCapacity(size + 1);
System.arraycopy(elementData, index, elementData, index + 1, size - index); // 数组中间添加元素
//平均时间复杂度O(n)
elementData[index] = e;
size++;
}
public E get(int index) {
if (index > size || index < 0)
return null;
return (E) elementData[index]; // 按位置获取元素 时间复杂度为O(1)
}
public E remove(int index) {
if (index > size || index < 0)
return null;
E oldValue = get(index);
int numMoved = size - index - 1;
if (numMoved > 0) // 删除元素 平均复杂度为O(n)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null;
return oldValue;
}
}
}
public E remove(int index) {
if (index > size || index < 0)
return null;
E oldValue = get(index);
int numMoved = size - index - 1;
if (numMoved > 0) // 删除元素 平均复杂度为O(n)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null;
return oldValue;
}
}
可见,ArrayList可以根据索引快速访问任意元素,但插入、删除、扩容操作会调用System.arrayCopy()方法,这是个非常占用系统资源的操作。当你遇到访问元素比插入或者是删除元素更加频繁的时候,你应该使用ArrayList,在频繁的插入或者是删除元素的情况下,LinkedList的性能会更加好一些。