ArrayList
Collection 系列文章的总目录:
- Collection 体系的三个核心约定
- Sorted & Navigable
- Iterator & Iterable
- Java 中的数组
- ArrayList
- LinkedList
- HashMap
- LinkedHashMap
- TreeMap
- HashSet/LinkedHashSet/TreeSet
使用 Array (数组) 作为内部存储:
- 速度快:寻址操作是 O(1) 的
- 扩容麻烦 (慢,费内存)
源码导读:
标记接口:
RandomAccess:
- ArrayList 实现了
RandomAccess
接口 标记接口
,空接口,用于告诉其他类,本类支持快速随机访问
- 作用:允许同时支持随机和串行访问的通用算法,通过改变内部的行为,来实现更好的性能
- 比如:
Collection.binarySearch()
二分查找
JDK 中 RandomAccess
接口的部分 JavaDoc:
Marker interface used by List implementations to indicate that they support fast (generally constant time) random access. The primary purpose of this interface is to allow generic algorithms to alter their behavior to provide good performance when applied to either random or sequential access lists.
......
As a rule of thumb, a List implementation should implement this interface if, for typical instances of the class, this loop:
for (int i=0, n=list.size(); i < n; i++)
list.get(i);runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
类似的标记接口
有:Cloneable
、Serializable
属性:
ArrayList
没有大小限制,默认和传入的容量值是指初始值- 如果没传入初始容量,或传入 0,不会马上创建 elementData[],只会在第一次 add() 的时候创建,避免浪费内存
// 默认初始容量为10
private static final int DEFAULT_CAPACITY = 10;
// 用于当外部指定容量为0时,或者size为0的时候进行trimToSize(),elementData的值
private static final Object[] EMPTY_ELEMENTDATA = {};
// 如果没有指定初始容量时,elementData的值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储元素的 Object[] 数组
transient Object[] elementData; // non-private to simplify nested class access
// 当前存在元素的个数
private int size;
transient
:Java 关键字,瞬变的。标识此字段不会参与序列化和反序列化。
Q:但是我们调用 ObjectOutputStream.writeObject()
的时候,发现 ArrayList
中的元素可以被序列化,这是为什么?
A:这是由于 ArrayList
内部实现了 writeObject()
和 readObject()
方法,从而实现自定义序列化和反序列化。
详情请看 Serializable
接口的 JavaDoc。
添加元素:
add(E e):
- 确保容量,如果不够,则扩容:ensureCapacityInternal
- 扩容:grow(),newCapacity = oldCapacity + (oldCapacity >> 1)
- 二进制右移,表示除以2,所以扩容 1.5 倍
- elementData[size++] = e
对于任何进制 n,左移一位等于:乘以 n;右移一位等于:除以 n。
比如:
- 十进制:1 << 1 = 10
- 二进制:01 << 1 = 10(二进制) = 2(十进制)
删除元素:
remove(int index):
- 检查 index:rangeCheck,IndexOutOfBoundsException
- 如果操作的不是最后一个元素,则将 index 之后的元素往前移动(复制)
- if (size - index - 1 > 0) : System.arraycopy
- 将 elementData[] 最后一个位置置空,防止内存泄漏
- elementData[--size] = null
- 返回删除的元素
并发访问异常:
关于ConcurrentModificationException
:
- 由于并发访问的存在,会导致操作数组的时候,预期和结果不一致
- checkForComodification() 方法会去对比 modCount 和 expectedModCount
- ArrayList 中散落着各种对比 modCount 和 expectedModCount 的代码,来检查并发访问
- 由于 ArrayList 本身就不是线程安全的,所以不要使用 CME 来保证程序的正确性
面试核心考点:
底层实现:数组
时间复杂度:
- 查找:使用 index 查找元素,O(1)
- 插入:需要移动 index 之后的元素,O(n)
- 删除:同上,O(n)