List<E>是Collection<E>接口的子接口。
让我们学习一波常用的List<E>接口的实现类吧,你都知道哪些?
今天我们学习四个:ArrayList、LinkedList、Stack、CopyOnWriteArrayList
在学之前,看看List的特点吧!
List接口:
List是有序的Collection,使用此接口可以精确的控制每个元素插入的位置,类似于数组,用户可以使用索引来访问List中的元素。和set不同,元素可以重复。
List相比Collection:
ArrayList(数组):
ArrayList实现了可变大小的数组,它允许所有的元素,包括null。ArrayList没有同步。
ArrayList线程不安全,内部不是原子操作。
Vector:
vector非常类似ArrayList,但是Vector是同步的。当一个线程正在迭代的时候,另一个线程改变Vector状态,会抛异常。
LinkedList(双向链表):
LinkedList实现了List接口,允许null元素,此外LinkedList提供了额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(Stack)、队列(Queue)或者双向队列(deque)
Stack:
stack继承Vector,先进后出的栈。
CopyOnWriteArrayList:
答:CopyOnWriteArrayList用于读多写少的并发场景,比如白名单、黑名单。
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
CopyOnWriteArrayList(免锁容器)的好处之一是多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException。在CopyOnWriteArayList中,写入将会导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。
问题1:ArrayList和LinkedList各自的特点,你怎么看?
问题2:ArrayList与LinkedList的使用场景和数据结构。
问题1:我感觉这个就跟问数组和链表的区别没什么区别,哈哈。
ArrayList其实是包装了一个数组 Object[],当实例化一个ArrayList时,一个数组也被实例化,当向ArrayList中添加对象是,数组的大小也相应的改变。这样就带来以下有缺点:
快速随即访问 你可以随即访问每个元素而不用考虑性能问题,通过调用get(i)方法来访问下标为i的数组元素。
向其中添加对象速度慢 当你创建数组是并不能确定其容量,所以当改变这个数组时就必须在内存中做很多事情。
操作其中对象的速度慢 当你要想数组中任意两个元素中间添加对象时,数组需要移动所有后面的对象。
LinkedList
LinkedList是通过节点直接彼此连接来实现的。每一个节点都包含前一个节点的引用,后一个节点的引用和节点存储的值。当一个新节点插入时,只需要修改其中保持先后关系的节点的引用即可,当删除记录时也一样。这样就带来以下有缺点:
操作其中对象的速度快 只需要改变连接,新的节点可以在内存中的任何地方
不能随即访问 虽然存在get()方法,但是这个方法是通过遍历接点来定位的所以速度慢。
说白了,就是数据结构中的顺序存储和链式存储
问题2:
List实现 使用场景 数据结构
ArrayList 数组形式访问List链式集合数据,元素可重复,访问元素较快 数组
LinkedList 链表方式的List链式集合,元素可重复,元素的插入删除较快 双向链表