今天无聊,学学ArrayList和LinkedList
文章目录
1.1 ArrayList底层实现
1.容量不固定,有最大阈值,但一般达不到
2.有序的(元素输出顺序与输入顺序一致)
3.元素可以为 null
4.效率高
5.由于基于数组实现,所以size(), isEmpty(), get(), set(), iterator(), ListIterator() 方法的时间复杂度都是 O(1) add() 添加操作的时间复杂度平均为 O(n) 其他所有操作的时间复杂度几乎都是 O(n)
6.占用空间更小,对比 LinkedList,不用占用额外空间维护链表结构,ArrayList的底层实现是基于数组的,所以它的get(int i)方法和set(int i, E e)方法都是通过索引直接定位到数组中, 所以这两个方法的时间复杂度都为O(1)常数级别,而remove(E e)方法的时间复杂度为O(n)级别,因为在remove的过程中需要遍历数组确定该元素的位置并进行删除,其他方法的时间复杂度也约为O(n) 级别
7.是Fail-fast机制的
8.是非同步的
E elementData(int index) {
return (E) elementData[index];
}
//获取
public E get(int index) {
//范围检查
rangeCheck(index);
//直接根据数组角标返回元素,时间复杂度为O(1)
return elementData(index);
}
//修改
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
//直接对数组操作
elementData[index] = element;
//返回原来的值
return oldValue;
}
public boolean add(E e) {
//对数组的容量进行调整
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
//根据位置删除
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;
}
public void add(int index, E element) {
//范围检查
rangeCheckForAdd(index);
//调整大小
ensureCapacityInternal(size + 1);
//将从index开始的元素向后复制移一位:
//1,2,3 add(1, 4) -> 1,2,2,3
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将index设置为element
//1,2,3 add(1, 4) -> 1,2,2,3 -> 1,4,2,3
elementData[index] = element;
size++;
}
//删除某个元素
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;
}
1.2 ArrayList基本参数
-
底层数据结构,数组
transient Object[] elementData -
数组初始化容量为10
private static final int DEFAULT_CAPACITY = 10; -
当前元素的个数
private int size; -
数组最大容量2^31 - 8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
1.3 grow(int capacity) 扩容方法(1.5倍扩容)
private void grow(int minCapacity) {
//旧数组的长度
int oldCapacity = elementData.length;
//新数组的长度为旧数组长度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新的扩容长读要大于ArrayList规定的最大长度,那么新长度则为int型最大长度
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//可以扩容,直接调用Arrays,copyOf方法(这个一个native方法),新开辟一块指定长度的
内存存储新数组元素
elementData = Arrays.copyOf(elementData, newCapacity);
}
例如在ArrayList中插入25个元素时,将会扩容3次:
size 11 —> 16 —> 23 —>25
capacity 10—>15—>22—>33
1.4 ArrayList具有RandomAccess的性质
所以使用for循环遍历获取元素会比使用迭代器获取元素的速度会更快一些(因为是基于设置的,若是基于链表的,那么使用迭代器遍历会快一些)
1.5 LinkedList基本概述
1.基于双端链表实现,底层节点为双向链表
2.删除元素和添加元素的时间复杂度为O(1),查找元素的时间复杂度为O(n)
3.允许元素为null
4.是有序的
5.是Fail-fast机制的
6.是非同步的
7.不需要扩容
//双向链表节点
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;
}
}
//整体是基于一个双端链表的
transient Node<E> first;
transient Node<E> last;
1.2 其他实现
其实就是链表的常规代码而已,需要注意迭代器中的modCount标识
ArrayList与LinkedList深入解析

本文详细对比了ArrayList和LinkedList两种Java集合类的底层实现、特性及操作效率。ArrayList基于数组,提供快速的随机访问,而LinkedList基于双端链表,擅长元素的添加和删除。
203

被折叠的 条评论
为什么被折叠?



