一、简介
ArrayList底层其实为一个一维数组,作为数组,ArrayList自然具备数组的特点:
- ArrayList元素在物理和逻辑上都是顺序的,即内存地址连续且存储有序;
- ArrayList元素必须为同一种类型;
- ArrayList获取元素得时间复杂度为O(1),插入、删除的时间复杂度为O(n)。
二、ArrayList 和 LinkedList 的区别
- 底层数据结构不同:
ArrayList基于数组实现,具有连续的内存空间。
LinkedList基于双向链表实现,每个结点都包含数据以及指向前后结点的引用。 - 随机访问元素效率不同:
ArrayList因为基于数组实现,所以可以通过数组下标访问元素,时间复杂度为O(1)。
LinkedList不支持随机访问,需要从头或尾遍历才能访问指定位置的元素,时间复杂度为O(n)。 - 插入和删除的效率不同:
ArrayList在中间位置插入或删除元素需要移动其他元素,性能较差,时间复杂度为O(n)。
LinkedList在任意位置插入或删除元素只需修改相邻节点的引用,性能较好,时间复杂度为O(1)。 - 内存占用不同:
ArrayList需要预先分配一定容量的连续内存空间,会浪费未使用的内存。
LinkedList每个节点都需要额外存储前后节点的引用,会带来更高的内存开销。
三、实现思路
3.1 基本设计
数组是一个固定的、连续的、线性的数据结构,那么想把它作为一个自动扩展容量的数组列表,则需要做一些扩展。
/**
* 默认初始化空间
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空元素
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* ArrayList 元素数组缓存区
*/
transient Object[] elementData;
/**
* List 集合元素数量
*/
private int size;
- 在初始化 ArrayList 时,我们可以显示的指定它的初始容量,如果不指定大小 ArrayList 会默认给数组初始化一个默认大小。
- 初始化默认大小的时机是哪呢?通过观察源码,我们不难发现:初始化默认大小并不是在 new ArrayList() 这个无参构造方法之中,而是在 add 方法,添加元素时初始化默认大小。
- 随着 ArrayList 的元素增加,不可避免的需要增加容量,这时也是 add 方法中进行扩容,同时还得需要把旧数据迁移到新的数组上。所以数据的迁移算是一个比较耗时的操作
3.2 添加元素
简易的 add 方法:
public boolean add(E e) {
// 1.保证容量
int minCapacity = size + 1;
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(minCapacity, DEFAULT_CAPACITY);
}
// 判断扩容操作
if (minCapacity - elementData.length > 0) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
elementData[size++] = e;
return true;
}
3.3 移除元素
ArrayList 的重点离不开对 System.arraycopy 的使用,它是一个本地方法,可以让你从原数组的特定位置,迁移到新数组的指定位置和迁移数量。如图 3-1 所示:
移除元素代码:
public E remove(int index) {
E oldValue = (E) elementData[index];
int numMoved = size - 1 - index;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null;
return oldValue;
}
四、关于 ArrayList 常见的面试题
- 数据结构中有哪些是线性表数据结构?
- 数组的元素删除和获取,时间复杂度是多少?
- ArrayList 中默认的初始化长度是多少?
- ArrayList 中扩容的范围是多大一次?
- ArrayList 是如何完成扩容的,System.arraycopy 各个入参的作用是什么?
- LinkedList插入速度比一定比ArrayList快吗?
欢迎大家一起在评论区讨论上述问题
(Tips:数据结构系列的源码已经更新到个人仓库地址:个人GitHub地址:https://github.com/Jing1558
个人Gitee地址:https://gitee.com/hzj072214/projects 文章中的代码实现都可以在个人仓库中找到源码,如果觉得有帮助请star)