ArrayList
特点
查询快
底层数据存储空间连续, 可以采用数组下标方法进行数据获取,数组+下标可以计算得到对应元素在内容的地址, CPU可以直接访问
增删慢
涉及到元素数据移动,移动操作浪费时间
增加操作可能导致底层数组扩容, 扩容操作需要时间和空间
删除操作导致有效元素个数和数组容量比例失衡, 导致缩容操作
数据结构
Object[]数组
非静态数组, 每个ArrayList都有自己的私有成员变量
transient Object[] elementData; // non-private to simplify nested class access
构造方法
默认容量: int DEFAULT_CAPACITY = 10;
无参默认空数组: Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
有参默认空数组: Object[] EMPTY_ELEMENTDATA = {};
//无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//初始化列表容量
//initialCapacity – 列表的初始容量
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);
}
}
//初始化列表
//c – 其元素要放入此列表的集合
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
添加元素
可能会移动元素 O(n)
可能会扩容 (1.5倍)
注意: 要分配的数组的最大大小。某些 VM 会在数组中保留一些标头字。尝试分配更大的阵列可能会导致内存不足错误:请求的阵列大小超过 VM 限制
MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//核心代码是扩容
//minCapacity – 所需的最小容量
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //预先申请,避免频繁扩容,空间换时间思想
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
//扩容1.5倍
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
扩容流程
1. 明确最小方法流程
2. 得到源数组需求
3. 计算得到新数组容量
int new Capacity = oldCapacity + (oldCapacity >> 1);
4. 判断新数组容量是否满足最小容量需求
5. 判断新数组容量是否超出最大容量范围
6. 根据源数组数据, 新数组容量, 调用copyOf方法创建新数组, 返回新数组地址
删除元素
可能需要移动元素 0(n)
缩容, 无自动缩容机制,需要[手动缩容]
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
查找元素
时间复杂度O(1)
public E get(int index) {
rangeCheck(index); //index是否合法
return elementData(index);
}
适用场景
有参构造, 读操作 >> 写操作
已知数据容量,可以在实例化 ArrayList 集合对象时,明确当前数据的容量存储范围,采用尾插法方式将数据逐一放入到集合中,对于数组元素增删慢弊端就可以避免,方便后期作为数组搜索,排序,查询等相关操作。
LinkedList
特点
头尾节点操作效率高
中间操,涉及节点遍历查询效率低, 导致整体性能差
数据结构
双向链表
private static class Node<E> {
E item; //当前节点数据
Node<E> next; //下一个节点对象
Node<E> prev; //上一个节点对象
}
//头节点对象
transient Node<E> first;
//尾节点对象
transient Node<E> last;
构造方法
//无参列表
public LinkedList() {
}
//有参列表
public LinkedList(Collection<? extends E> c) {
this();
addAll(c); ==> addAll(size, c)
}
添加元素
没有扩容操作
头插法和尾插法
头: addFirst(E)
尾: add(E) addAll( Collection<E> ) addLast(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++;
}
//尾插法
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++;
}
//add(int index, E element)的核心
//查找
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;
}
}
//插入
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++;
}
删除元素
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;
}
查找元素
时间复杂度O(n)
Node<E> node(int index) {
// assert isElementIndex(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;
}
}