推荐阅读时间:10分钟
简介
ArrayList 是日常开发中很常见的集合类型,在 Java 集合中相对容易阅读。它是基于数组实现的一种列表,读取、修改的时间复杂度很小(O(1)), 插入、remove 时时间复杂度为O(n)。ArrayList 可以存放 null 值,列表清空就是通过把所有的元素置为 null 实现的。
Arrays.copyOf() 和 System.arraycopy()
首先我们先看下代码里反复出现的两个方法:Arrays.copyOf() 和 System.arraycopy()。其实 public static <T> T[] copyOf(T[] original, int newLength)
是通过调用后者实现的,输入待拷贝的数组和要返回数组的长度,拷贝出一个新的数组。而public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length)
是真正执行拷贝的方法。它是个 native 方法,五个参数分别代表 待拷贝数组、待拷贝数组的起始位置、目标数组、目标数组的插入位置、拷贝的长度。每次拷贝都是全量拷贝,因此容量变化的操作较多时,会对它造成性能影响。
RandomAccess
ArrayList 实现了 RandomAccess 接口表示支持快速随机访问,将使用 for 循环查找元素。如果没有实现该接口(如 LinkedList),在查找时,只能通过 迭代器 进行查找,查找速度要低于前者。
Java doc 中具体解释如下:1
2
3
4
5
6
7
8
9* <pre>
* for (int i=0, n=list.size(); i < n; i++)
* list.get(i);
* </pre>
* runs faster than this loop:
* <pre>
* for (Iterator i=list.iterator(); i.hasNext(); )
* i.next();
* </pre>
⭐注释版源码⭐
1 |
|
writeObject 和 readObject
ArrayList 实现了 Serializable 接口,所以对象会被序列化。而存放元素的 elementData 中可能会存在元素数量比数组容量小很多的情况,序列化时就会造成大量的空间浪费,因此通过实现 writeObject 和 readObject 方法,即可重新定义序列化与反序列化的规则。ArrayList 在 elementData 前加上了 transient 取消其默认序列化规则,其他属性则执行默认的规则。
迭代器
iterator()
方法会返回一个 Iterator 迭代器,遍历时较常见。
源码如下:
1 | /** |
listIterator(int index)
、listIterator()
这两个方法返回的是 ListIterator 迭代器,与 Iterator 相比,它支持反向遍历和 add() 方法,比较容易理解,不再赘述。
此外,还有一个 spliterator()
方法,它返回的是一个 Java8 新加的 Spliterator 迭代器。Spliterator 是一个可分割迭代器(splitable iterator),为了并行遍历元素而设计。如果有机会我们再分析它。
sublist
ArrayList 提供的public List<E> subList(int fromIndex, int toIndex)
方法允许返回一个子list。
根据注释得知:
- 该方法返回的是父list的一个视图,从fromIndex(包含),到toIndex(不包含)。fromIndex=toIndex 表示子list为空
- 父子list做的非结构性修改(non-structural changes)都会影响到彼此:所谓的“非结构性修改”,是指不涉及到list的大小改变的修改。相反,结构性修改,指改变了list大小的修改。
- 对于结构性修改,子list的所有操作都会反映到父list上。但父list的修改将会导致返回的子list失效。
- tips:删除list中的某段数据的方法:
list.subList(from, to).clear();
觉得有点收获的同学可以在手机上点击这个链接 免费领取一杯咖啡(瑞幸咖啡券,使用后我也得一张😃)