ArrayList
要点
实现机制
数组
扩容机制
初始容量为空列表,第一次插入后扩容成默认大小 10。
添加元素时如果已满,会自动扩容为原始大小的 1.5 倍。
类定义
java
// 类定义
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- 实现了
RandomAccess
接口,支持随机访问。
RandomAccess
是一个标志接口,说明该类支持快速随机访问
- 实现了
Cloneable
接口,默认为浅拷贝。 - 实现了
Serializable
接口,支持序列化。 - 非线程安全:可以使用
Collections.synchronizedList()
包装成线程安全的
数据结构
- elementData:对象数组(用于存数据)
- size:当前数组长度
- DEFAULT_CAPACITY:默认大小
java
// 默认初始化容量
private static final int DEFAULT_CAPACITY = 10;
// 对象数组
transient Object[] elementData;
// 数组长度
private int size;
构造方法
- 无参构造:默认初始大小(10)
- 指定初始大小构造:减少数组的扩容次数,提高性能
java
public ArrayList() {
// 创建一个空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 根据初始化值创建数组大小
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 初始化值为 0 时,创建一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
访问元素
通过下标获取,复杂度 O(1)
java
// 获取第 index 个元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
添加元素
- 尾部添加:直接放在数组最后
- 任意位置添加:向后复制后半段来腾出当前位置
默认大小为 10,超过数组大小会触发扩容 1.5 倍。
java
// 添加元素到数组末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 添加元素到任意位置
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
ArrayList 的扩容机制:
java
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// new = old * 1.5
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);
}
删除元素
删掉当前位置元素,将后半段向前复制
java
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; // clear to let GC do its work
return oldValue;
}
Fail-Fast 机制
使用 modCount
来记录结构发生变化的次数,用来避免并发修改异常。
LinkedList
要点
实现机制
基于双向链表:顺序访问会非常高效,而随机访问效率比较低。
类定义
java
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 实现了
Deque
接口,也可以被当作队列Queue
或双端队列Deque
进行操作 - 实现了
Cloneable
接口,默认为浅拷贝。 - 实现了
Serializable
接口,支持序列化。 - 非线程安全:可以使用
Collections.synchronizedList()
包装成线程安全的
数据结构
- size:数组长度
- first、last:双向链表头尾节点
Node:链表的节点
java
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
// ...
}
// 链表长度
transient int size = 0;
// 链表头节点
transient Node<E> first;
// 链表尾节点
transient Node<E> last;
序列化
访问元素
通过 size 和 index 判断 Node 是在前半段还是后半段,再遍历链表。时间复杂度 O(n)
java
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
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;
}
}
添加元素
- add、addLast:尾插
- addFirst:头插
- add(index, item):指定位置插入
java
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++;
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
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++;
}
public boolean add(E e) {
linkLast(e);
return true;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size) linkLast(element);
else linkBefore(element, node(index));
}
public void addFirst(E e) {
linkFirst(e);
}
public void addLast(E e) {
linkLast(e);
}
删除元素
遍历找到要删除的元素节点,然后调用 unlink
方法删除节点
- 前驱节点指向后继,否则更新头指针;
- 后继节点指向前驱,否则更新尾指针。
java
public boolean remove(Object o) {
if (o == null) {
// 遍历找到要删除的元素节点
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
// 遍历找到要删除的元素节点
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {
// assert x != null;
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;
}
Vector
基于数组实现,特性与ArrayList类似。(不再推荐使用)
线程安全:使用线程同步能力,多线程互斥写入Vector。
全部操作方法都加的有 synchronized 关键字,性能雪崩。
https://blog.csdn.net/weixin_44688973/article/details/119732347
List
Arrays.asList()
-
不能转换基本类型的数组:数组会被当成一个对象
-
返回的 List 不能增删:返回的不是正常的 ArrayList,没有重写 add 和 remove 方法
-
原始数组的修改会影响 List:转换后直接复用了原始的数组
-
不能直接使用 Arrays.asList() 来转换基本类型数组。
Arrays.asList() 方法传入的是一个泛型 T 的可变参数,会导致数组整体作为了一个对象成为了 T
java
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
- 返回的 List 不支持增删操作
Arrays.asList() 返回的 List 并不是的 java.util.ArrayList
,而是 Arrays 的内部类 ArrayList。没有重写 add 和 remove 方法。
- 对原始数组的修改会影响到我们获得的那个 List
Arrays.asList() 转换后直接复用了原始的数组
List.subList()
用途:截取集合中的一部分
问题:
- subList 直接引用了原始的 List,而不是一个新的 List,操作会相互影响
- 如果原 List 在 subList 操作期间发生了结构修改(增删操作),操作 subList 会抛异常
解决:
- 使用新的集合 new ArrayList
- 使用 stream 流的 limit 进行操作