目录
List
1.概述
List 是一个有序序列,除了继承了 Collection 接口的方法之外,还有下面的功能拓展:
- 位置访问(positional access),基于索引来操作元素,包含get、set、add、addAll 和 remove。
- 搜索,在 List 中搜索指定的元素,放回元素的索引,indexOf、lastIndexOf。
- 迭代,利用 List 顺序的特点,对 Iterator 的语义进行了拓展,比如 ListIterator。
- 范围视图(range-view),用于执行范围操作,sublist
2.功能拓展
位置访问
基本的位置访问方法是get、set、add、remove。(set 和 remove 会返回元素以前的值)。
典型的例子是 Conllections 中的 shuffle 函数实现:
搜索
迭代
List 的 listIterator 方法返回了一个针对于 List 的迭代器 ListIterator,可以进行双向迭代。ListIterator 的定义如下:
ListIterator 的 cursor 并不指向元素,而是指向元素之间或 List 两端,所以,对于一个长度为 n 的 List,cursor 会有 n + 1 个位置。
对于 ListIterator 的方法,hasNext、hasPrevious 分别返回是否存在下一个、上一个元素。next、previous 分别返回下一个、上一个元素,并移动迭代位置。nextIndex、previousInde 分别返回下一个、上一个元素的索引。
remove 方法,将上次调用 next 、previous 函数返回的 element 给修改为新的元素。每次调用 next 或 previous 只能进行一次此调用。在此之中不能调用 add 方法,否则会抛出异常。
set 方法,将上次调用 next 、previous 函数返回的 element 给修改为新的元素。在此之中不能调用 remove、add 方法,否则会抛出异常。
add 方法,添加一个新元素到 next 与 previous 返回元素之间(如果有的话),cursor 位于新元素之后。
AbstractList 接口中有一个成员变量为 modCount,它记录了 List 被修改的次数,用于子类某些实现,要求对于迭代器使用过程中,根据此值是否改变来确认迭代器是否快速失效,而不是去发现是否存在其它线程修改原 List。
范围视图
List 的 subList 返回 [fromIndex, toIndex) 的范围视图(fromIndex 与 toIndex 相等时返回空列表)。
需要注意到的是:
- subList 的非结构化改变会影响原 list,反之亦然。
- 原 list 有除 subList 以外的任何方式在结构上进行了修改,那么 subList 的行为会变得未定义。
所以,一般将 subList 作为一个 transient object 来使用,而不是长期持有。
3.实现
Java 提供了两种通用的实现:ArrayList 和 LinkedList。在绝大多数情况下,都应该选择 ArrayList,只有在频繁的在List中插入和删除元素时,才考虑使用 LinkedList。
其它实现这里先不做叙述。
ArrayList
ArrayList 是一个基于能动态改变容量的数组实现的 List,类似于 C 中动态申请内存。
实现原理
首先,我们查看 ArrayList 的成员变量,主要的如下:
其中,Array 中的元素其实是被存储到 elementData 数组中,并对元素做了类型擦涂,为 Object,所以 ArrayList 中可以存储不同类型的元素。size 的值为 elementData 中的元素数量(并非大小)。
elementData 会有 3 种值:
- EMPTY_ELEMENTDATA:表示,当明确 ArrayList 列表的大小为 0 时。此时所有的 ArrayList 实例共享 EMPTY_ELEMENTDATA。
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA:表示,未明确指出 ArrayList 列表大小,采用的默认容器。注意,ArrayList 使用 Lazy Initialization 的方式申请内存,初始化时 elementData 为空,只有当插入第一个元素时,才会创建默认大小的底层数组。
- new Object[length]:指明了列表长度时。
ArrayList 需要确保列表大小,以保证有足够的空间来存储元素。这里就使用了扩容的机制,当需要扩容时,会开启新大小的空间并将原列表复制进去。有关扩容的方法如下:
其中,ensureCapacity 是提供给程序员使用的,ensureCapacityInternal 是当我调用 ArrayList中的其它方法时,如 add,程序调用的函数。这两个会去调用 ensureExplicitCapacity 方法进行扩容。扩容执行函数 grow,策略为变为 1.5 倍。
CopyOnWriteArrayList
ArrayList 并不是线程安全的,我们可以使用 Collections 中的同步包装器进行装饰为同步的。
Vector 虽然是线程安全的,但他的实现原理为给方法加 synchronized,简单除暴,性能差。
CopyOnWriteArrayList 是将数据的读与写分开,读操作实现类似于 ArrayList,但对于写操作,进行了加锁。add 代码示例如下:
对于写操作,加锁后,会把原数组拷贝一份,在新的数组上进行修改,然后更新原列表,解锁。
对于 CopyOnWriteArrayList 的迭代器,其实拿到的是获取是的 snapshot 版本,数组被保存到 COWIterator 中。在使用迭代器的过程中,其它线程修改了原列表时,由于修改后其实是给了一个新数组,原数组并未改变,所以修改原列表对迭代并没有影响。
需要注意的是,CopyOnWriteArrayList 获得的迭代器,不支持 remove、set、add 方法。
LinkedList
与 ArrayList 不同的是,LinkedList 除了实现 List,也实现 Deque。但 LinkedList 未实现 RandomAccess,不支持快速随机访问,说明 LinkedList 使用迭代器访问比 for 循环更快,这与 ArrayList 相反。
实现原理
LinkedList 其实是实现了一个双向链表。size 存储了链表大小,first 与 last 分别记录头节点与尾节点。
LinkedList 中每个节点用 Node 表示。Node 中,item 为真实元素,next、prev 分别表示链表中上一个与下一个节点。
LinkedList 中涉及到查询某个 index 的节点会使用的 node 方法。对于实现,首先会根据 index 判断节点离那端近,然后从对应端进行查询:
其它方法也基于双端链表实现。
标签:Java,LinkedList,迭代,ArrayList,元素,List,add,集合 From: https://www.cnblogs.com/meyok/p/16939281.html