ArrayList
要点
实现机制
数组
扩容机制
初始容量为空列表,第一次插入后扩容成默认大小 10。
添加元素时如果已满,会自动扩容为原始大小的 1.5 倍。
类定义
// 类定义
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:默认大小
// 默认初始化容量
private static final int DEFAULT_CAPACITY = 10;// 对象数组
transient Object[] elementData;// 数组长度
private int size;
构造方法
- 无参构造:默认初始大小(10)
- 指定初始大小构造:减少数组的扩容次数,提高性能
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)
// 获取第 index 个元素
public E get(int index) {rangeCheck(index);return elementData(index);
}E elementData(int index) {return (E) elementData[index];
}
添加元素
- 尾部添加:直接放在数组最后
- 任意位置添加:向后复制后半段来腾出当前位置
默认大小为 10,超过数组大小会触发扩容 1.5 倍。
// 添加元素到数组末尾
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 的扩容机制:
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 codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;// new = old * 1.5int 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);
}
删除元素
删掉当前位置元素,将后半段向前复制
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 workreturn oldValue;
}
Fail-Fast 机制
使用 modCount
来记录结构发生变化的次数,用来避免并发修改异常。
LinkedList
要点
实现机制
基于双向链表:顺序访问会非常高效,而随机访问效率比较低。
类定义
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:链表的节点
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)
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):指定位置插入
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
方法删除节点
- 前驱节点指向后继,否则更新头指针;
- 后继节点指向前驱,否则更新尾指针。
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
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 进行操作