文章目录
  1. 1. List
  2. 2. AbstractList
    1. 2.1. equals方法
    2. 2.2. hashCode方法
  3. 3. ArrayList
    1. 3.1. 定义
    2. 3.2. 构造器
    3. 3.3. 动态扩增
    4. 3.4. add方法
    5. 3.5. remove方法
    6. 3.6. 迭代器
    7. 3.7. Collections的同步方法
  4. 4. Vector
    1. 4.1. 定义
    2. 4.2. 构造器
    3. 4.3. 动态扩增
    4. 4.4. 线程安全性
  5. 5. Stack
  6. 6. 总结

JDK容器类的线性表主要分两种:一种是以ArrayList为代表的顺序存储线性表;另一种是以LinkedList为代表的链式存储线性表。

List

上篇提到过List接口在Collection的基础之上增加了大量的方法,主要用来支持在序列中间插入和删除元素。下面列出List接口的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface List<E> extends Collection<E> {
// 此处省略Collection中定义的方法
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
// 此处省略其他方法
}

AbstractList

AbstractList继承自AbstractCollection父类,并且实现了List接口。

equals方法

AbstractList重写了equals方法,List判断equals的标准是遍历每一个元素,依次调用对象的equals方法判断,代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}

hashCode方法

AbstractList重写了hashCode方法,hashcode的计算公式为:
hashCode计算公式

1
2
3
4
5
6
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}

ArrayList

定义

首先看ArrayList的定义,其继承自AbstractList,实现了List、RandomAccess、Cloneable、Serializable接口。

1
2
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

  • RandomAccess是一个标识接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。
  • Cloneable接口,实现了此接口的类,可以调用Object.clone完成对象的浅复制。
  • Serializable也是一个标识接口,实现了此接口的类,支持对象的序列化。

构造器

前面提到,ArrayList基于数组实现。ArrayList中只有两个私有属性,如下:

1
2
transient Object[] elementData;  // 使用一个对象数组来保存,transient意味着不被序列化
private int size; // 记录当前的长度

在ArrayList初始化的时候,如果没有指定长度,默认为一个空数组。

1
2
3
4
5
6
7
8
9
10
11
12
private static final Object[] EMPTY_ELEMENTDATA = {}; // 空ArrayList共享此数组
public ArrayList(int initialCapacity) { // 指定长度的初始化
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
super();
// 默认为空数组
this.elementData = EMPTY_ELEMENTDATA;
}

动态扩增

当数组长度不够的时候,ArrayList会调用grow方法先以1.5倍速度扩增数组,然后再将数据拷贝到新的数组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 调用者可以通过此方法,主动增大数组的长度
public void ensureCapacity(int minCapacity) {
// 当初始化为空数组情况时,返回默认长度10
int minExpand = (elementData != EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
// 在ArrayList的add、addAll等方法中,都会调用此方法检查数组容量是否足够。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 最终都会调用此方法,以1.5倍速度扩增数组
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// newCapacity = oldCapacity * 1.5,1.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); // 调用Arrays.copyOf方法拷贝数组
}
// 此方法定义可以看出,list的最大长度(数组的最大长度)为Integer.MAX_VALUE
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

数组拷贝使用了工具类Arrays的copyOf方法,追踪源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// java.util.Arrays#copyOf
public static <T,U> T[] copyOf(U[] original, int newLength,
Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength)); // 调用了System.arraycopy进行拷贝
return copy;
}

// java.lang.System#arraycopy
public static native void arraycopy(Object src,
int srcPos,Object dest, int destPos, int length)
;

可以看到最终调用了System.arraycopy方法完成数组的拷贝。这是一个native方法,意味着虚拟机已经使用C++实现了数组拷贝的功能。设计者考虑到数组拷贝是个相当频繁的操作,采用了此种方式实现是为了提高拷贝的效率。

add方法

1
2
3
4
5
6
7
8
9
10
11
12
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 检查容量是否足够,否则扩增
elementData[size++] = e; // 当前size位赋值为新增对象,size后移一位
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index); // 检查index是否越界,越界会抛出IndexOutOfBoundsException
ensureCapacityInternal(size + 1); // 检查容量是否足够,否则扩增
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}

add的总体思路:1)检查是否越界;2)检查容量是否足够;3)插入位置后面元素后移;4)插入位置赋值插入对象。

remove方法

1
2
3
4
5
6
7
8
9
10
public E remove(int index) {
rangeCheck(index); // 检查index是否越界,越界会抛出IndexOutOfBoundsException
modCount++; // 修改次数加1,modCount变量在父类AbstractList中定义
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // 末尾赋值null,以便GC时清空占用内存
return oldValue;
}

remove的思路:1)检查是否越界;2)修改modCount;3)删除位置后面元素前移;4)末尾置null。此处只列出了E remove(int index)方法的实现,其余remove方法如boolean remove(Object o)、boolean removeAll(Collection<?> c)思路类似。需要注意的是所有的remove方法都修改了modCount值,下面会讲到此值的作用!

迭代器

所有的Collection都提供了迭代器的实现,用于遍历访问Collection的元素。List额外支持双向迭代ListIterator。
iterator结构图
ArrayList的迭代器结构如上图所示,典型的迭代器模式。迭代器实现类Itr和ListItr为ArrayList的内部类。
ArrayList的迭代遵循fail-fast机制。当迭代遍历过程中,如果使用其他方法如remove改变List结构,就会抛出ConcurrentModificationException,从源码可以看出这种安全检查基于之前提到的modCount。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private class Itr implements Iterator<E> {
int cursor; // 遍历用指针
int lastRet = -1;
// 初始化迭代器时,用expectedModCount记录当前modCount
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification(); // 检查是否有并发修改
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); // 检查是否有并发修改
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // 更改expectedModCount值以保证安全
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
// 通过比较modCount和expectedModCount,判断是否存在并发修改,如果有的话抛出异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

  • 其他Collection的迭代器模式实现均类似于ArrayList
  • 其他Collection的迭代器fast-fail机制均类似于ArrayList

Collections的同步方法

Collections工具类为集合提供了各种工具方法,其中synchronizedXxx()方法用于把各种线程不安全的容器变为线程安全的。以List为例,这种实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
static class SynchronizedList<E>
extends SynchronizedCollection<E> implements List<E> {

final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}
public int indexOf(Object o) {
synchronized (mutex) {return list.indexOf(o);}
}
public int lastIndexOf(Object o) {
synchronized (mutex) {return list.lastIndexOf(o);}
}
public boolean addAll(int index, Collection<? extends E> c) {
synchronized (mutex) {return list.addAll(index, c);}
}
// 此处省略其他方法
}

可以看出来,synchronizedList方法构造的线程安全容器,仅仅是对每个线程不安全的方法使用synchronized关键字修饰。其线程安全的原理同Vector。

  • 其他Collections中的同步方法原理均类似于List

Vector

Vector是JDK早期版本的基于数组实现的线性表。

定义

1
2
3
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

从定义来看,Vector和ArrayList完全一样,继承自AbstractList,实现了List、RandomAccess、Cloneable、Serializable接口。

构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected Object[] elementData;  // 用Object数组保存对象
protected int elementCount; // elementCount记录当前对象个数
protected int capacityIncrement; // 每次扩增时的增量
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() { // 默认长度为10
this(10);
}

动态扩增

1
2
3
4
5
6
7
8
9
10
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity); // 以固定单位或者2倍速度扩增数组
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}

Vector的自动扩增策略与ArrayList稍有不同。ArrayList每次以1.5倍速度扩增;Vector如果初始化时指定了capacityIncrement,则每次以该变量值为单位扩增,否则每次扩增为原来长度的2倍。

线程安全性

Vector和ArrayList最大的区别是Vector本身是线程安全的。从源码可以很清楚的看到Vector如何实现线程安全的,即用synchronized修饰所有可能造成不安全访问的方法。

1
2
3
4
5
6
7
8
public synchronized int capacity() {...}
public synchronized boolean isEmpty() {...}
public synchronized int indexOf(Object o, int index) {...}
public synchronized E get(int index) {...}
public synchronized E set(int index, E element) {...}
public synchronized E remove(int index) {...}
public void add(int index, E element) {...}
// 此处省略其他方法

Vector的迭代器实现及fail-fast机制同ArrayList。

Stack

Stack也是JDK早期版本基于Vector实现的栈。Stack继承自Vector,额外提供了以下用于栈的访问方法。

1
2
3
4
5
public E push(E item) {...}
public synchronized E pop() {...}
public synchronized E peek() {...}
public boolean empty() {...}
public synchronized int search(Object o) {...}

Stack基于Vector实现,因此Stack也是线程安全的。

总结

  • ArrayList和Vector都是基于数组实现的线性表,支持以一定策略动态扩增。
  • ArrayList和Vector的迭代器采用标准迭代器模式实现,遵循fast-fail机制。
  • 在单线程环境中,由于不存在并发访问的问题,使用Stack或者Vector会降低访问效率。推荐使用ArrayList和后面会提到的LinkedList。

源码解读基于JDK-8

文章目录
  1. 1. List
  2. 2. AbstractList
    1. 2.1. equals方法
    2. 2.2. hashCode方法
  3. 3. ArrayList
    1. 3.1. 定义
    2. 3.2. 构造器
    3. 3.3. 动态扩增
    4. 3.4. add方法
    5. 3.5. remove方法
    6. 3.6. 迭代器
    7. 3.7. Collections的同步方法
  4. 4. Vector
    1. 4.1. 定义
    2. 4.2. 构造器
    3. 4.3. 动态扩增
    4. 4.4. 线程安全性
  5. 5. Stack
  6. 6. 总结