上述类图中,实线边框的是类,而点线边框的是接口。
//继承关系
java.lang.Object
java.util.AbstractCollection
java.util.AbstractList
java.util.ArrayList
//实现接口
List, Iterable, Collection, Serializable, Cloneable, RandomAccess
/**
* 存储ArrayList元素的数组缓冲区。
* ArrayList的容量是此数组缓冲区的长度。
*/
private transient Object[] elementData;
ArrayList底层依赖数组。从源码中可以看出,它封装了一个Object[]类型的数组。而数组的优点就是方便查询。
public ArrayList() {
this(10);
}
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList(Collection extends E> c) {
......
}
第一个无参构造:调用第二个构造方法,所传参数就是默认初始化集合容量,大小为10;
第二个带参构造:自定义集合容量;
第三个带参构造:这里不进行说明。
//将指定的元素追加到此集合的末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//将指定元素插入此集合的指定位置
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
这里只讲解两个常用添加方法。
第一个添加方法:在集合末尾添加。
第二个添加方法:在集合指定位置添加。
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
首先,检测index是否在集合范围内,若不在,抛出IndexOutOfBoundsException;
然后直接根据index查找数组中的对应的值,并返回。
通过上面的add方法可知,每次在添加元素之前都会进行扩容检测。下面根据源码来分析ArrayList集合具体是怎么进行扩容的。
扩容操作的第一步:
private void ensureCapacityInternal(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);
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底层依赖数组来实现,查询效率较高,增删效率较低
ArrayList中的元素有序、可重复、允许null值
ArrayList会自动进行扩容1.5倍。初始化时尽量指定初始容量,可避免频繁扩容,影响程序执行效率
线程不安全,适用于单线程环境。
//继承关系
java.lang.Object
java.util.AbstractCollection
java.util.AbstractList
java.util.Vector
//实现接口
List, Collection, Iterable, Serializable, Cloneable, RandomAccess
protected Object[] elementData;
Vector的底层实现与ArrayLIst一样,也是依赖数组。
public Vector() {
this(10);
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
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(Collection extends E> c) {
......
}
第一个无参构造:调用第二个构造方法,继而调用第三个构造方法,所传参数就是默认初始化集合容量,大小为10;
第二个带参构造:调用第三个构造方法,自定义集合容量;
第三个带参构造:自定义集合容量,并设置容量增长量,如果增量大于0,则在扩容后的容量为原容量+增量。
第四个带参构造:这里不进行说明。
//将指定的元素追加到此Vector的末尾。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
Vector只有在集合末尾添加单个元素的方法(当然,它有在指定位置添加一个集合的方法,这里不作讲述),与ArrayList的add()方法类似。
有一点区别就是Vector的扩容检测方法是ensureCapacityHelper(elementCount + 1)。
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
同ArrayList的get方法类似。
Vector的扩容与ArrayList有一些不同,主要是多了一个成员变量capacityIncrement,下面重点讲与ArrayList不同的地方。
protected int capacityIncrement;
扩容操作的第一步:同ArrayList类似
private void ensureCapacityHelper(int minCapacity) {
// 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 + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
从源码中第三行代码可以看出Vector具体的扩容方法:
在对集合进行初始化的时候,若没有通过构造方法对capacityIncrement赋值(默认为0),则集合扩容后的容量为原来的2倍;
否则,集合扩容后的容量为原容量+增量
Vector底层依赖数组来实现,查询效率较高,增删效率较低
Vector中的元素有序、可重复、允许null值,添加单个元素的话,只能添加到集合末尾
Vector会自动进行扩容。扩容后的容量由增量来决定,(2倍 or 原容量+增量)
大多数方法用关键字synchronized修饰,线程安全。
//继承关系
java.lang.Object
java.util.AbstractCollection
java.util.AbstractList
java.util.AbstractSequentialList
java.util.LinkedList
//接口实现
List, Queue, Deque, Collection, Iterable, Serializable, Cloneable
transient Node first;
transient Node last;
LinkedList底层依赖链表。从源码中可以看出,它封装了头结点、尾节点。而链表的优点就是方便增删节点。
若面试官细问,可以回答LinkedList底层依赖双向循环链表。因为双向链表包含两个指针,pre指向前一个节点,next指向后一个节点。 由下面源码可知,第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,形成一个“环”。
private void linkFirst(E e) {
final Node f = first;
final Node newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public LinkedList() {
}
public LinkedList(Collection extends E> c) {
this();
addAll(c);
}
第一个无参构造:LinkedList集合容量默认为空,没有扩容的概念。
第二个带参构造:这里不进行说明。
//将指定的元素追加到此列表的末尾。
public boolean add(E e) {
linkLast(e);
return true;
}
//将指定元素插入此列表中的指定位置。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//返回此列表中指定位置的元素。
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
首先,进行异常检测,若有异常,抛出IndexOutOfBoundsException;
因为LinkedList无法随机访问,只能通过遍历的方式找到相应的节点;
从源码中可以看出,为了提高效率,当前位置首先和元素数量的中间位置开始判断,小于中间位置,从头节点开始遍历,大于中间位置从尾节点开始遍历。
LinkedList底层依赖双向循环链表实现,增删效率较高,查询效率较低
LinkedList中的元素有序、可重复、允许null值
线程不安全,适用于单线程环境。
ArrayList、Vector底层依赖数组,查询效率较高,增删效率较低(因为Vector是线程安全的,整体效率比ArrayList低)
LinkedList底层依赖双向循环链表,增删效率较高,查询效率较低
ArrayList、Vector、LinkedList中的元素有序、可重复、允许null值
ArrayList一次扩容1.5倍
Vector根据增量扩容,增量为0,扩容2倍;否则原容量+增量
LinkedList没有扩容
ArrayList、LinkedList线程不安全(如果有多个线程需要同时访问List集合中的元素,可以考虑使用Collections将集合包装成线程安全的集合)
Vector线程安全