Java集合大体能够分为Set、List、Queue和Map四种体系,其中Set表明无序、不可重复的集合;List表明有序、重复的集合;而Map则表明具备映射关系的集合,Java 5 又增长了Queue体系集合,表明一种队列集合实现。node
Collection家族:数组
List集合安全
List集合是有序、可重复的集合数据结构
本篇文章将集中介绍List集合的两个重要子类ArrayList(而且同时对比Vector)和LinkedList。框架
1.List集合中,判断元素是否相等须要集合元素类重写equals()方法。函数
2.List集合中,咱们可使用ListIterator来提供更加厉害的表里操做,ListIterator相比Iterator,能够对集合元素在遍历期间进行修改、插入、删除操做,而且还能够向前遍历。这些在Iterator中是没法作到的,其缘由是由于List的底层采用数组结构实现,用数组意味着就有标记来记录各个元素的位置,所以针对List集合能够经过ListIterator在遍历期间进行集合元素的修改操做。性能
3.ArrayList与Vector的相同点在于底层都为数组实现,而且的默认初始长度为10。不一样地方在于,进行扩容时,ArrayList每次扩容增长当前容量的50%,Vector则增长一倍。ArrayList为线程不安全,Vector为线程安全。ArrayList遍历方式有Iterator、ListIterator、for循环、加强for循环,Vector遍历方式有Iterator、ListIterator、for循环、加强for循环、枚举Enumeration。测试
咱们分别看下Vector与ArrayList的初始化构造函数:spa
再看下Vector与ArrayList的add方法,观察其扩容策略:线程
Vector
Vector进行扩容操做的逻辑为
若是此向量的当前容量小于minCapacity,则经过将其内部数组替换为一个较大的数组俩增长其容量。
新数据数组的大小姜维原来的大小 + capacityIncrement,
除非 capacityIncrement 的值小于等于零,在后一种状况下,新的容量将为原来容量的两倍,不过,若是此大小仍然小于 minCapacity,则新容量将为 minCapacity。
capacityIncrement为容量增量,就是每次Vector进行扩容的长度,在Vector进行初始化的时候能够指定capacityIncrement,不然默认为0。也就是扩容一倍。
ArrayList
ArrayList的扩容逻辑除了标红处以外,其他和Vector相同。
4.若是开始就知道ArrayList或Vector集合须要保存多少个元素,则能够在建立它们时就指定initalCapacity初始长度的大小,这样能够提升性能。
此外,ArrayList还提供了两个额外的方法来调整其容量大小:
void ensureCapacity(int minCapacity) //若有必要,增长此 ArrayList 实例的容量,以确保它至少可以容纳最小容量参数所指定的元素数。
void trimToSize() //将此 ArrayList 实例的容量调整为列表的当前大小。
5.Stack是Vector的子类,用于模拟“栈”这种数据结构,“栈”一般是指“后进先出”(LIFO)的容器。最后“push”进栈的元素,将被最早“pop”出栈。Stack与Vector同样,是线程安全的,性能较差,尽可能少用Stack类。若是要实现栈”这种数据结构,能够考虑使用LinkedList。
6.LinkedList类是List接口的实现类——这意味着它是一个List集合,能够根据索引来随机访问集合中的元素。除此以外,LinkedList还实现了Deque接口(继承了Queue接口的双端队列),能够被看成成双端队列来使用,所以既能够被当成“栈"来使用,也能够当成队列来使用。
LinkedList的实现机制与ArrayList彻底不一样。ArrayList内部是以数组的形式来保存集合中的元素的,所以随机访问集合元素时有较好的性能;而LinkedList内部以链表的形式来保存集合中的元素,所以随机访问集合元素时性能较差,但在插入、删除元素时性能比较出色。
7.LinkedList调用默认构造函数,建立一个链表。因为维护了一个表头,表尾的Node对象的变量。能够进行后续的添加元素到链表中的操做,以及其余删除,插入等操做。也所以实现了双向队列的功能,便可向表头加入元素,也能够向表尾加入元素。
下面来了解Node类的具体状况
由此能够具体了解链表是如何串联起来而且每一个节点包含了传入集合的元素。
下面以增长操做,具体了解LinkedList的工做原理。
调用linkLast(e);方法,默认向表尾节点加入新的元素
更新表尾节点,创建链接。其余操做相似,维护了整个链表。
下面具体来看,如何将“双向链表和索引值联系起来的”?而且为何说LinkedList经过下标访问效率低?
调用了node(index)方法返回了一个Node对象,其中node(index)方法具体以下
首先会比较“index”和“双向链表长度的1/2”;若前者小,则从链表头开始日后查找,直到index位置;不然,从链表末尾开始先前查找,直到index位置。这就是“双线链表和索引值联系起来”的方法。
到此咱们便会明白,LinkedList在插入、删除元素时性能比较出色,随机访问集合元素时性能较差。
8.LinkedList遍历方式
LinkedList支持多种遍历方式。
.经过迭代器遍历LinkedList
.经过快速随机访问遍历LinkedList(低效)
.经过for循环遍历LinkedList
.经过pollFirst()遍历LinkedList
.经过pollLast()遍历LinkedList
.经过removeFirst()遍历LinkedList
.经过removeLast()遍历LinkedList
实现都比较简单,就不贴代码了。
其中采用逐个遍历的方式,效率比较高。采用随机访问的方式去遍历LinkedList的方式效率最低。
LinkedList也是非线程安全的。
9.ArrayList与LinkedList性能对比
ArrayList 是一个数组队列,至关于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。ArrayList应使用随机访问(即,经过索引序号访问)遍历集合元素。
LinkedList 是一个双向链表。它也能够被看成堆栈、队列或双端队列进行操做。LinkedList随机访问效率低,但随机插入、随机删除效率高。LinkedList应使用采用逐个遍历的方式遍历集合元素。
若是涉及到“动态数组”、“栈”、“队列”、“链表”等结构,应该考虑用List,具体的选择哪一个List,根据下面的标准来取舍。
(01) 对于须要快速插入,删除元素,应该使用LinkedList。
(02) 对于须要快速随机访问元素,应该使用ArrayList。
(03) 针对迭代的效率问题
LinkedList:高级for循环与Iterator迭代(效率类似)>下标访问迭代。
ArrayList:下标访问迭代>高级for循环与Iterator迭代(效率类似)。
测试数据以下: 基于JDK1.8
LinkedList,ArrayList分别经过上面三种方式,遍历10w次String对象的效率
遍历100w次
遍历300w次
Iterator遍历会被JVM编译为
而高级for循环会被编译为
(04) 数组和链表的遍历效率问题(后续讨论。。。)