【Java集合】List分析
ArrayList分析
ArrayList是由动态的数组实现的
访问元素速度比较快,删除和增加元素速度较慢
ArrayList的默认容量是 10
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
如果能够预估ArrayList的容量,创建ArrayList对象时指定初始化的容量大小initialCapacity,这样能避免重新增大集合的容量,可以提高List的效率。
- 向ArrayList添加(add)元素时,先检查容量是否小于所需要的最小长度,如果小于,则长度增长0.5倍。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 容量增长为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
- ArrayList不是线程安全的,可以使用集合工具类将的synchronizedList方法将其包装为线程安全的。
List list = Collections.synchronizedList(new ArrayList(...));
- 迭代器iterator和反向迭代器listIterator,只能被“消费”一次
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
参考
LinkedList分析
- LinkedList继承的类和实现的接口
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
它的功能十分强大,可以作为链表、栈、队列、双端队列使用。
- 有头结点和尾结点”指针”
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
链表有头结点的引用first很正常,它可以代表这个链表。
尾结点的引用last发挥一个重要的作用,方便在链表的末尾添加元素,只需要last的后继设置为新的元素,之后将last指向该新元素(新的尾结点)即可,非常快速。
- 功能强大,方法很多,实现一个功能可能会有多种方式,现在梳理出常用的方法
// 当作为list使用时,声明引用的类型为 List<E>
public boolean add(E e)
public boolean remove(Object o)
public E get(int index)
// 当作为stack使用时,声明引用的类型为 LinkedList<E>,因为Stack跟LinkedList并没有什么关系
public void push(E e)
public E pop()
public E peek()
// 当作为Queue使用时,声明引用类型为 Queue<E>,虽然LinkedList并没有实现Queue接口,
// 但是它实现的Deque接口继承了Queue接口
public boolean offer(E e)
public E poll()
public E peek()
说明,
当push时,LinkedList会把新元素设置为新的头结点
当offer时,LinkedList会把新元素设置为新的尾结点
当LinkedList用作栈和队列时,均使用public E peek()访问第一个元素,第一个元素表示栈顶或者队头,总之它是链表的头结点。
- LinkedList不是线程安全的,和ArrayList一样,可以使用集合工具类将的synchronizedList方法将其包装为线程安全的。
List list = Collections.synchronizedList(new LinkedList(...));
Vector分析
- Vector和ArrayList功能基本相同。
- Vector是线程同步的。
- 默认容量是10
- 容量小于最小需求时,将扩大容量为来的二倍。
- 两种迭代方式
Vector 继承 List的 iterator 和 listIterator 方法所返回的迭代器是快速失败的
Vector 的 elements 方法返回的 Enumeration 不是快速失败的。
小结
ArrayList
- 本质上是一个可改变大小的数组,当元素加入时,其大小将会动态地增长;
元素顺序存储 ,随机访问很快,删除非头尾元素慢,新增元素慢而且费资源 - 非线程安全.
- 较适用于无频繁增删的情况,比数组效率低,如果不是需要可变数组,可考虑使用数组。
LinkedList
- 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能,但在get与set访问数据方面弱于ArrayList.
- 非线程安全
- 适用于,没有大规模的随机读取,大量的增加/删除操作
Vector
- 和一样ArrayList,使用数组顺序存储元素。
- 但其是同步的,开销就比ArrayList要大。
如果你的程序本身是线程安全的,那么使用ArrayList是更好的选择。
Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.
小结部分参考
参考
JDK ArrayList,LinkedList、Deque、Queue、Vector源码
笔记