JDK 文档中对Vector的描述是这样的:Vector 类实现了一个可增长的对象数组。像数组一样,可以通过整型索引来获取内容,但是 Vector 的大小可以按照实际元素数量的需求进行增长或收缩。Vector 和 ArrayList 非常接近,区别在于 ArrayList 的实现是非同步的,在多线程环境中可能出现线程不安全的情况,而 Vector 则是线程安全的。
Stack 类实现的就是我们非常常用的一种数据结构,栈,也称之为*后进先出队列(LIFO)*。栈的基本概念自不必多说,Java 中 Stack 类是基于 Vector 来实现的。
实际上,Vector 和 Stack 类已经不被推荐使用了,之所以仍然还保留是出于兼容性方面的考虑。通常情况下可以使用 ArrayList 来替代 Vector,在一些需要保证线程安全的情况下在外部进行同步或者使用Collections.synchronizedList
方法。至于 Stack,官方的推荐是使用 Deque 接口的实现进行替代。
概览
Vector 类和 Stack 类的声明如下:
1
2
3
|
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
class Stack<E> extends Vector<E>
|
从类的声明来看,Vector 类和 ArrayList 是完全一致的,实际上它们俩的内部实现也是大致相同的,只是 Vector 类中的所有方法都使用 synchronized
关键字进行了修饰。
Stack 就是 Vector 的子类,对栈的所有操作 pop()
、push()
、peek()
都是在 Vector 的基础上进行实现的。
底层结构
Vector 类的底层也是是用数组进行数据存储的,同时维护了一个整型变量记录目前容器中元素的数量。除此以外,还使用了一个整型变量记录扩容时应该增加的容量大小。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement;
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);
}
|
从扩容方法grow()
中可以看到,如果在构造函数中没有指定每次扩容时应该增加的容量大小,则每次扩容默认为容量加倍。这个和 ArrayList 中不太一样,ArrayList每次扩容是容量默认增加0.5倍。
Satck 的操作
由于 Stack 是在 Vector 的基础上实现的,而 Vector 又是基于数组进行实现的,栈顶就可以看作数组的尾部。Stack 类的实现非常的简略,下面简要地列出栈操作的内部实现。
入栈
入栈即通过 push(E item)
方法向栈中压入一个元素,其实现方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//Satck.java
public E push(E item) {
addElement(item);
return item;
}
//Vector.java
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
|
其实就是向数组尾部添加一个元素。
获取栈顶元素
peek()
方法的实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//Stack.java
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
//Vector.java
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
}
|
就是返回数组尾部的最后一个有效元素。
出栈
将栈顶的元素弹出栈,pop()
,实现如下:
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
|
//Stack.java
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
//Vector.java
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
|
就是移除数组尾部的最后一个元素,并返回该元素。
查询
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
|
//Stack.java
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
//Vector.java
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
if (o == null) {
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
|
小结
Vector 类的实现和 ArrayList 比较接近,都是基于数组及扩容操作来完成的。只是 Vector 类为了保证线程安全性,在每个方法的声明中都使用了 synchronized
关键字,这势必会影响它的性能。Satck 类则是扩展了 Vector 类,实现了栈的各种操作。由于 Stack 类继承自 Vector,则同样继承了 Vector 类的各种方法。这个其实比较奇怪,因为对栈操作只需要入栈和出栈操作就可以了,完全可以不使用继承的方式,而使用包含的方式,即在Satck对象中使用 Vector 的引用。之所以采用了继承的方式,估计还是因为历史的包袱吧。
官方已经不推荐使用 Vector 和 Stack。要使用线程安全的 List,可以用 Collections.synchronizedList
来 包裹 ArrayList。至于栈,则推荐使用 Deque 接口的实现,如 LinkedList 和 ArrayDeque。