从类的定义浅析
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
public class Vector
extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
public abstract class AbstractSequentialList extends AbstractList {
首先从继承父类来看:
ArrayList & Vector 都继承了AbstractList 抽象类(提供了
List 接口的默认实现,支持随机访问);
LinkedList 继承了 AbstractSequentialList(提供了
List 接口的简化实现,简化在只支持按次序访问);
其次从实现接口来看:
①、三者都实现了 List 接口(定义了列表必须实现的方法)、Cloneable 接口(可以调用Object.clone方法返回该对象的浅拷贝)、java.io.Serializable 接口(支持序列化和反序列化);
②、ArrayList & Vector 两者都实现了 RandomAccess 接口(提供了快速随机访问存储的元素的功能),而 LinkedList 实现了 Deque 接口(支持双向队列)
从类的属性浅析
ArrayList.java
private static final int DEFAULT_CAPACITY = 10; // 默认容量
transient Object[] elementData; // 装元素的容器,不可序列化
private int size; // ArrayList 的大小
Vector.java
protected Object[] elementData; // 装元素的容器
protected int elementCount; // Vector 的大小
protected int capacityIncrement; // 每次 Vector 容量增加时的增量值
LinkedList.java
transient int size = 0; // LinkedList 的大小
transient Node first; // 头指针
transient Node last; // 尾指针
// 结点的定义
private static class Node {
E item; // 结点值
Node next; // 下一个结点
Node prev; // 前一个结点
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
由上源码可知:
①、ArrayList & Vector 两者底层的数据结构是数组结构,所以改查很快,增删较慢(又因为 Vector 是线程同步的,所以增删改查都比 ArrayList 慢)。而 LinkedList 的底层的数据结构是链表结构,所以改查较慢,增删较快。
②、ArrayList & Vector 底层都维护了一个 Object[] 对象数组用于存储对象,默认数组的长度是 10。但是 ArrayList 的对象数组,通过 transient 修饰,不可序列化。
③、Vector 维护了一个 capacityIncrement 字段,可以通过构造器去设置每次扩容的大小,而 ArrayList 没有。
从类的扩容方法浅析
ArrayList.java
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 扩容
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 数组扩容:增大 0.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
Vector.java
public synchronized boolean add(E e) {
modCount++;
// 扩容方法
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 数组扩容:增大 1 倍(capacityIncrement 表示每次 Vector 容量增加时的增量值)
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
由上源码可知:
ArrayList 扩容时,容量自动增长为原来的容量的 1.5 倍,而 Vector 的容量自动增长为原来的容量的 2 倍。
从遍历的方法浅析
三者都支持三种遍历方式:for-each()(几乎与迭代器一样)、迭代器方式、下标递增或递减循环。
ArrayList,下标递增或递减循环方式是速度最快的,但是 for-each() 实现更加简单,且速度没慢太多,推荐使用。
LinkedList,for-each() 方式是速度最快的。
总结
①、
ArrayList & Vector 支持随机访问;
LinkedList 只支持按次序访问;
②、
三者都定义了列表必须实现的方法、可以调用Object.clone方法返回该对象的浅拷贝、支持序列化和反序列化;
③、
ArrayList & Vector 提供了随机访问功能;
LinkedList 支持双向队列;
④、
ArrayList & Vector 是数组结构,所以改查很快,增删较慢(又因为 Vector 是线程同步的,所以增删改查都比 ArrayList 慢)。
LinkedList 是链表结构,所以改查较慢,增删较快。
⑤、
ArrayList & Vector 底层都维护了一个 Object[] 对象数组用于存储对象,默认数组的长度是 10。
但是 ArrayList 的对象数组,通过 transient 修饰,不可序列化。
⑥、
Vector 维护了一个 capacityIncrement 字段,可以通过构造器去设置每次扩容的大小,而 ArrayList 没有。
⑦、
ArrayList 扩容时,容量自动增长为原来的容量的 1.5 倍;
Vector 扩容时,容量自动增长为原来的容量的 2 倍。
⑧、
无论哪种,都推荐使用 for-each() 遍历的方式。
注意:Vector 属于遗留容器,已经不推荐使用。
Q:但 ArrayList 和 LinkedListed 都是非线程安全的,所以如果遇到多个线程操作同一个容器的场景,该怎么处理?
A:可以通过工具类 Collections 中的 synchronizedList() 方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。