List接口
List接口,成为有序的Collection也就是序列。该接口可以对列表中的每一个元素的插入位置进行精确的控制,同时用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。例如List接口定义如下方法,实现元素指定位置的插入和访问。
- add(int index, E element)
- get(int index)
AbstractList 抽象类
List 接口的骨干实现,以最大限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作。
List具体实现类
Vector
类声明如下
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
构造方法如下
Constructor 1
public Vector() {
this(10);
}
Constructor 2
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
Constructor
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < )
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
Constructor 4
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
Constructor 1:创建一个容量为 10,增量为 0 的 Vector 容器,容量和增量的概念接下来会介绍。
Constructor 2:创建一个容量为形参 initialCapacity,增量为 0 的 Vector 容器。
Constructor 3:创建一个容量为形参 initialCapacity,增量为 capacityIncrement的Vector 容器。
Constructor 4:创建一个容量为集合 c 长度,增量为 0 的 Vector 容器。
上面四个构造方法中使用到了Vector定义的三个成员变量:
protected Object[] elementData;
Object类型的数组,Vector底层是数组来实现的,所有的元素都在 elementData 数组中存放
protected int elementCount;
elementCount 属性用来记录Vector容器中的元素个数,也就是数组 elementData 中元素的个数,而不是 elementData 数组的长度,下面是Vector size()方法的定义,直接返回elementCount 的值。
public synchronized int size() {
return elementCount;
}
protected int capacityIncrement;
capacityIncrement 属性记录容器的增量,即容器自动扩展的时候一次性扩展的大小。
接下来看一下 Vector 类元素增加、删除、访问和迭代实现的具体细节
1 增加元素
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + );
elementData[elementCount++] = e;
return true;
}
1 synchronized:Vector在多线程环境下提供了同步机制。
2 add 方法并没有进行null判断,允许null元素。
modCount++:modCount 属性在 AbstractList 中定义
protected transient int modCount = ;
modCount 是用来记录容器被结构性修改的次数,所谓结构性修改是对容器进行了元素增加和删除等操作,下面是Vector修改和获取元素的代码都没有进行 modCount++ 的操作。
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return (E) elementData[index];
}
public synchronized E set(int index, E element) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
Object oldValue = elementData[index];
elementData[index] = element;
return (E)oldValue;
}
那设置这个属性的作用是什么呢?是为了在多线程环境下以一种叫做“快速失败”的方式通知程序迭代遍历容器失败。下面是调用 Vector 容器的 iterator()方法返回的迭代器对象的具体类。
private class Itr implements Iterator<E> {
int cursor = ;
int lastRet = -;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
此类通过 expectedModCount 保存了一个 Vector 结构化修改的次数,而在迭代过程中会不断的调用 checkForComodification 方法,一旦发现容器被结构化修改就会抛出ConcurrentModificationException 异常。可以联想,当线程 t1 正在迭代遍历 Vector 容器时,线程 t2 结构性修改了容器,那么线程 t1 将立刻抛出异常,这也是所谓的“快速失败”。
接着上面讲 Vector add 方法的第二行代码:
ensureCapacityHelper(elementCount + );
在往容器中添加元素之间需要确保容器有足够的空间来存储元素,即 elementData 数组还有空间存储元素,ensureCapacityHelper 方法代码如下:
private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = (capacityIncrement > ) ? (oldCapacity + capacityIncrement)
: (oldCapacity * );
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
elementCount + 1 代表容器存放下当前元素需要的最小空间,如果最小空间尚且大于数组的长度,那么必须进行动态扩容,扩容的方法如上所示。
接着 Vector add 方法的第三行代码
这个代码完成了元素的添加。
2 删除元素
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
Object oldValue = elementData[index];
int numMoved = elementCount - index - ;
if (numMoved > )
System.arraycopy(elementData, index+, elementData, index,numMoved);
elementData[--elementCount] = null;
return (E)oldValue;
}
此处关键的操作,是 System.arraycopy 方法,此方法是一个 native 方法,主要完成数组元素的移动,即从 index+1 开始,每个元素往前移动一位。
因为index后面的元素都往前移动了一位,所以需要将最后一位制空,elementCount 属性值减一。
3 访问元素
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return (E)elementData[index];
}
代码简单易懂,此处不再介绍。
4 迭代元素
Vector 没有重写 iterator 方法,直接继承 AbstractList 类的iterator方法,定义如下:
public Iterator<E> iterator() {
return new Itr();
}
内部类 Itr 定义如下所示
private class Itr implements Iterator<E> {
int cursor = ;
int lastRet = -;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
Iterator remove 操作不会引起 当前线程迭代遍历失败,因为删除元素之后重新设置了 expectedModCount 的值。
next 和 hasNext 方法简单易懂。
3 Vector迭代遍历,按照加入元素的顺序输出所有元素。
4 Vector加入元素时并没有进行排序。
List<Integer> vector = new Vector<Integer>();
vector.add();
vector.add();
vector.add();
vector.add();
vector.add();
for(Integer i: vector){
System.out.println(i);
}
所以代码输出结果为:
5
4
3
2
1
Vector#总结
- 提供同步机制
- 底层数组实现
- 可以加入null元素
- 元素加入时没有进行排序
- 元素可以重复加入
Stack
类声明如下
Stack通过五个操作对Vector进行扩展,允许将向量视为堆栈
- empty() 测试堆栈是否为空
- peek() 查看堆栈顶部的对象,但不从堆栈中移除它
- pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象
- push(E item) 把项压入堆栈顶部
- search(Object o) 返回对象在堆栈中的位置,以 1 为基数
Stack构造函数
public Stack() {
}
只有一个空构造函数
因为 Stack 继承自 Vector ,所以兼备Vector所有的特性,下面我们看一下 Stack 扩展的 5 中基本操作。
1 push(E item) 将元素推入栈顶
public E push(E item) {
addElement(item);
return item;
}
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + );
elementData[elementCount++] = obj;
}
push 方法调用 Vector 中定义的方法将当前元素添加到Statck容器中数组的末尾,也就是栈结构对应的栈顶。
2 peek() 查看堆栈顶部的对象,但不从堆栈中移除它
public synchronized E peek() {
int len = size();
if (len == )
throw new EmptyStackException();
return elementAt(len - );
}
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return (E) elementData[index];
}
peek 方法调用 Vector 中定义的 elementAt 方法返Statck容器中数组最后一个元素,即栈顶元素。
3 pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - );
return obj;
}
pop 方法调用 Vector 方法中定义的 removeElementAt 方法删除并返回Statck容器中数组的最后一个元素,即栈顶元素。
4 empty() 测试堆栈是否为空
public boolean empty() {
return size() == ;
}
5 search(Object o) 返回对象在堆栈中的位置,以 1 为基数
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= ) {
return size() - i;
}
return -;
}
从栈顶处开始检索,即栈顶元素的返回 1,次栈顶元素返回 2 。
Stack<Integer> stack = new Stack<Integer>();
stack.push();
stack.push();
stack.push();
stack.push();
stack.push();
System.out.println(stack.search());
System.out.println(stack.search());
1
5
Stack总结
- 继承自 Vector
- 提供同步机制
- 扩展了 push、pop、 empty、peek、search 五个方法
ArrayList
类声明如下
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
构造方法
Constructor 1
public ArrayList() {
this(10);
}
Constructor
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < )
throw new IllegalArgumentException("Illegal Capacity: "
+ initialCapacity);
this.elementData = new Object[initialCapacity];
}
Constructor 3
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
构造方法与 Vector 差不多,这里不再赘述。
ArrayList 成员属性
private transient Object[] elementData;
Object 类型的数组,ArrayList 底层是数组来实现的,所有的元素都在 elementData 数组中存放.
private int size;
size 属性用来记录 ArrayList 容器中的元素个数,也就是数组 elementData 中元素的个数,而不是 elementData 数组的长度。
接下来看一下 ArrayList 类元素增加、删除、访问和迭代实现的具体细节
1 增加元素
public boolean add(E e) {
ensureCapacity(size + );
elementData[size++] = e;
return true;
}
这跟 Vector 的 add 方法基本上一致,添加元素之前要确保空间够用,然后将元素加入容器,size++。
1 no synchronized:ArrayList 不是多线程安全的。
2 add 方法并没有进行null判断,允许null元素。
2 删除元素
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - ;
if (numMoved > )
System.arraycopy(elementData, index + , elementData, index,
numMoved);
elementData[--size] = null;
return oldValue;
}
跟 Vector 中定义的删除方法基本一致,首先是越界检查,然后数组元素移位。
3 访问元素
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
代码简单明了,不再赘述。
4 迭代元素
Arraylist 没有重写 iterator 方法,同样使用 AbstractList 定义的方法返回容器迭代器,跟上述的 Vector 完全一样。
ArrayList 总结
- 不提供同步机制
- 底层数组实现
- 可以加入null元素
- 元素加入时没有进行排序
- 元素可以重复加入
LinkedList
LinkedList 与 ArrayList 一样实现 List 接口,只是 ArrayList 是 List接 口的大小可变数组的实现,LinkedList 是 List 接口链表的实现。基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些。
AbstractSequentialList提供了 List 接口的骨干实现,从而最大限度地减少了实现受“连续访问”数据存储(如链接列表)支持的此接口所需的工作,从而以减少实现List接口的复杂度。
除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
与栈结构相关的方法
public void push(E e);
public E pop();
public E peek();
LinkedList 实现了 Deque 接口,Deque 接口继承自 Queue 接口,LinkedList中与单端对列相关的此方法
boolean add(E e);
E poll();
E peek();
LinkedList与双端队列相关的方法
双端队列:即在队列两端都可以 insert 和 remove:insertLast、insertFirst,removeLast、removeFirst 含有栈和队列的功能,如去掉 insertLast、removeLast,那就跟栈一样了;如去掉insertLast、removeFirst,那就跟队列一样了。
public void addFirst(E e) ;
public void addLast(E e);
public E removeFirst();
public E removeLast();
1 LinkedList 提供了栈、队列及双端队列数据结构的实现。
LinkedList
类声明
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinedList 构造方法
Constructor1
public LinkedList() {
header.next = header.previous = header;
}
Constructor2
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
在介绍构造方法之前首先看一下 LinkedList 的成员属性
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = ;
size 属性记录 LinkedList 容器的尺寸
header 属性是一个 LinkedList 内部类 Entry 类型,接下来看一下 Entry 类的定义
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
element 是元素的值
next 是当前元素后继的引用
previous 是当前元素前驱的引用
接下来继续探讨 LinkedList 的构造方法
Constructor2 中显示调用 Constructor1 方法,Constructor1 执行了这样一条语句
header.next = header.previous = header;
初始化成员属性 header,为下面添加元素做准备

接下来看一下 LinkedList 类元素增加、删除、访问和迭代实现的具体细节
1 增加元素
public boolean add(E e) {
addBefore(e, header);
return true;
}
private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
创建一个新的 元素节点。
接下来的几行代码完成新元素的添加
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
为了更清楚的展示,在向 LinkedList 中添加一个元素 B
2 LinkedList 底层使用循环双链表实现。
3 元素添加时没有进行排序。
4 元素添加时没有提供同步机制。
5 没有限制 null 元素的添加。
2 删除元素
public E remove() {
return removeFirst();
}
public E removeFirst() {
return remove(header.next);
}
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
删除操作,简单明了,只不过是在循环双链表上进行元素前驱和后继引用的设置,但要明确一点,header 并不是容器中的有效元素,因为其 element 为 null。它只是一个辅助元素。
3 访问操作
public E get(int index) {
return entry(index).element;
}
private Entry<E> entry(int index) {
if (index < || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size);
Entry<E> e = header;
if (index < (size >> )) {
for (int i = ; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
get 方法调用 entry 方法来定位目标元素在LinkedList中的位置,因为 LinkedList 底层双循环链表实现的,所以可以根据 index 参数的大小从前往后检索、或者从后往前检索。
4 迭代操作
LinkedList 没有重写 iterator 方法 ,继承 AbstractSequentialList 中定义的 iterator 方法,下面看一下 AbstractSequentialList 中的 iterator 方法的定义
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
if (index< || index>size())
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
iterator 方法调用 listIterator 方法,最后生成一个 ListItr 类型的迭代器,下面是 ListItr 类型的定义
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
public boolean hasPrevious() {
return cursor != ;
}
public E previous() {
checkForComodification();
try {
int i = cursor - ;
E previous = get(i);
lastRet = cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - ;
}
public void set(E e) {
if (lastRet == -)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
AbstractList.this.add(cursor++, e);
lastRet = -;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
ListItr 扩展了如下方法
public boolean hasPrevious();
public E previous();
public int nextIndex();
public int previousIndex();
public void set(E e);
public void add(E e);
这里我们不在讨论上述 六种方法,只关心与迭代紧密相关的 hashNext和next方法,但是在 ListItr 类中没有发现这两个方法。因为 ListItr 继承自 Itr(在 AbstractList 中定义) 类,所以 hashNext 和 next 方法跟上面讲述 Vector 迭代时一致。
LinkedList 总结
- 底层是循环双联表实现
- 没有提供同步机制
- 允许 null 元素
- 加入元素时没有进行排序
- 直接继承自 AbstractSequentialList
- 元素可以重复加入