聊一聊Vector与Stack
Java Collection系列下面有List,Set,Queue等,而Vector属于List下面的一个实现。
特点
- Vector本质上是一个可变数组
- 在Vector创建之后,其size可以增加和减少
- 线程安全的
- 在非多线程的情况下建议使用ArrayList
简要分析
先看一下有哪些重要的属性:
protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement;
这其中elementData是一个数组,用来实际存储数据;
容量方面有两个概念,一个是elementCount,一个是capacityIncrement;
其中elementCount代表的是中有效元素的个数,也就是说从elementData[0]到elementData[elementCount - 1]中的是实际的数据项。
而capacityIncrement代表的是,当你需要扩容的时候,一次性增加的容量大小。比如,在你向Vector中插入大量数据之前,可以增加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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
实际扩容的时候,调用的是grow方法。
如果 capacityIncrement > 0 ,则容量新增capacityIncrement大小;
如果 capacityIncrement <=0 , 则容量新增一倍;
然后再对最小应该满足的容量,和最大的容量配置进行判断,保证稳定性。
其中还有一个方法,可以直接设置容量:
public synchronized void setSize(int newSize) {
modCount++;
if (newSize > elementCount) {
ensureCapacityHelper(newSize);
} else {
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
elementCount = newSize;
}
如果新容量 > 当前的容量,则进行扩容,然后将null元素添加到Vector后面;
如果新容量 <= 当前的容量,则从索引值为newSize的开始,后面的元素全部设置为null;
说完了Vector,就顺便说一下Stack吧。
Stack
Stack继承自Vector,在其基础之上添加了一些入栈,出栈的操作,如push()/pop()/peek()等。
其也是线程安全的;
public
class Stack<E> extends Vector<E> {
public Stack() {
}
public E push(E item) {
addElement(item);
return item;
}
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
public boolean empty() {
return size() == 0;
}
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
private static final long serialVersionUID = 1224463164541339165L;
}
从Stack的源码中可以看到,它的入栈,出栈,查询操作均是利用Vector中的实现方法,并且都是同步的,因此是线程安全,但是性能是有所损耗。
通常可以利用Deque或者Deque的实现类来替换Stack,比如:
Deque<Integer> stack = new ArrayDeque<Integer>();
其中ArrayDeque并非线程安全的,这一点在使用的时候得注意,而且它不支持null元素。
ArrayDeque可以用来作为栈或者队列:
用作Stack的时候,它比Stack快;
用作Queue的时候,它比LinkedList(LinkedList本身实现了Deque接口,因为可以作为Queue来使用)快;