一、相同点
首先我们要知道,不管是ArrayList,LinkedList还是Vestor他们都是java.util.List接口的实现类。换而言之他们三个就是具有List特征的集合,List集合的特点就是他们三个的相同点。
我们通过查询API可以的到List接口的描述:
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
我们可以将其简略的总结为:List是有序的,元素可重复的集合。请注意这里的有序指的是元素存入集合的顺序和取出的顺序一致。
二、不同点
我将从源码中为大家简单讲解他们的特点让大家能更加直观的认识到他们的不同之处
1.ArrayList
在源码中我们首先可以看到
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
也就是说ArrayList是AbstractList同时是支持泛型的。
在大多数情况下我们通常使用 ArrayList的构造方法来创建一个ArrayList对象
在JAVA中ArrayList提供了三个构造方法分别是
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
在这里我们仅拿无参构造进行分析
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;
ArrayList定义只定义类两个私有属性:
elementData和size
elementData存储ArrayList内的元素,size表示它包含的元素的数量
当我们在给ArryList 放入元素时ArryList 会数组的大小自动对数组进行扩容具体过程如下
//添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
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++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容长度0.5倍
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);//数组扩容
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
从这里我们可以看出,ArrayList实际上是使用数组在存储数据的,也就是说ArrayList具有数组的特性。同时每次扩容0.5倍
简单来说,ArrayLis是实现了基于动态数组的数据结构,由于使用索引从指定的位置(用index)检索一个对象,他的查询效率很高,由于在非末尾插入和删除时需要重新移动数据,所以他的删除或者插入的效率比较低。每次自增0.5倍容量
2.Vector
我们还是先看看Vector的构造方法
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}
可以看得出来,Vector如ArrayList一样初始化了一个Object数组用于存放数据同时。规定了如果向量的大小大于其容量时,容量自动增加的量。
那么Vector和ArrayList都是使用数组来存放数据,他们又有什么区别呢。
继续翻看源码,可以发现,Vector和ArrayList都有很多相同名称的方法但是存在一定的区别,以add()方法举例,对比如下
//Vector
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
//ArrayList
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
我们可以发现Vector的方法是加锁的,这保证了他可以保证他的线程是安全的,与此相对的他的效率会比不加锁的ArrayList相对逊色。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
可以看出Vector每次自增一倍的容量
由于Vector具有ArrayList的所有功能,所以在一般情况下我们可以把Vector当做线程安全的ArrarList使用,每次扩容增大一倍容量,效率低于ArrayList.
3.LinkedList
LinkedList可以直译为链表,顾名思义LinkedList一定是和链表脱不开关系的
在API中对LinkedList是这样描述的:List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
事实上LinkedList是一个双向的链表,那么他肯定会具备链表的特点
在插入、删除集合中任何位置的元素所花费的时间都是一样的,但它在索引一个元素的时候比较慢。
transient Node<E> first;
transient Node<E> last;
我们用add和get方法举例简单的了解一下LinkedList的工作原理
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
LinkedList的工作原理并不是该文重点所以在此不再赘述了。我们需要知道的是由于LinkedList是一个双链表,所以如果我想得到他里面的一个元素我需要一个个的去找,当我需要对这个插入和删除一个元素的时候只需要修改他的节点而不需要对数据进行挪动。
由此我们可以总结出LinkedList的特点:LinkedLis是实现了基于双链表的数据结构,在插入、删除集合中任何位置的元素所花费的时间都是一样的,但它在索引一个元素的时候比较慢。
还有一点,由于LinkedList实现了 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。我们也可以用LinkedList来模拟队列和栈。