文章目录
- 概述
- Vector
- 成员变量
- 关键方法
- 添加元素 (add, addElement)
- 删除元素 (remove)
- 注意事项
- Stack
- 类定义与继承
- 压栈 (`push`)
- 弹栈 (`pop`)
- 查看栈顶元素 (`peek`)
- 源码解析总结
概述
在Java中,Stack 类是一个基于 Vector 实现的经典栈数据结构。由于 Vector 类本身是线程安全的,并且是一个动态数组,因此继承自 Vector 的 Stack 类自然也就具备了这些特性
Vector
Java中的Vector类是一个线程安全的动态数组,它继承自AbstractList并实现了RandomAccess、Cloneable和Serializable、List接口。Vector内部使用一个数组来存储元素,并且设计为在多线程环境下安全使用。
它允许我们存储任何类型的对象,并提供了动态增长的能力。以下是Vector类的一些关键源码解析和重要特性:
成员变量
elementData:一个Object类型的数组,用于存储Vector中的元素。
elementCount:表示当前Vector中实际元素的数量。
capacityIncrement:每次Vector需要扩容时,除了默认的加倍扩容外,额外增加的容量大小。
关键方法
添加元素 (add, addElement)
public synchronized void addElement(E obj) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = obj;
}private void ensureCapacityHelper(int minCapacity) {if (minCapacity - elementData.length > 0)grow(minCapacity);
}private void grow(int minCapacity) {// 计算新容量,至少为原容量的两倍或满足最小容量需求int oldCapacity = elementData.length;int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 扩容操作elementData = Arrays.copyOf(elementData, newCapacity);
}
删除元素 (remove)
public synchronized boolean remove(Object o) {return removeElement(o);
}private boolean removeElement(Object obj) {modCount++;for (int i = 0; i < elementCount; i++) {if (obj.equals(elementData[i])) {fastRemove(i);return true;}}return false;
}private void fastRemove(int index) {modCount++;int numMoved = elementCount - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--elementCount] = null; // Let gc do its work
}
注意事项
Vector中的大多数修改方法(如add, remove)都是同步的,这意味着它们在多线程环境中是线程安全的,但这也可能引入性能开销。
由于线程安全的设计,Vector在单线程环境下相比非线程安全的集合(如ArrayList)性能较差。
capacityIncrement参数允许用户自定义每次扩容的增长量,如果不指定,默认情况下每次扩容都会使容量翻倍。
当不再需要Vector对象时,应确保正确地将其设置为null,以便垃圾回收器能够回收其占用的内存资源。
Stack
Java中的Stack
类是一个基于Vector
实现的类,用于表示后进先出(LIFO,Last-In-First-Out)的数据结构。它提供了基本的栈操作,如压栈(push)、弹栈(pop)和查看栈顶元素(peek)。以下是Stack
类的一些关键源码解析:
类定义与继承
public class Stack<E> extends Vector<E> {// 构造方法public Stack() {super();}// ...
}
压栈 (push
)
public E push(E item) {addElement(item); // 直接调用Vector的addElement方法添加元素到数组末尾return item;
}
这里,push
方法实际上调用了Vector
类的addElement
方法,该方法负责在数组的末尾添加一个元素。由于Vector
是动态数组,如果数组空间不够,它会自动扩容。
弹栈 (pop
)
public synchronized E pop() {E obj;int len = size();obj = peek(); // 获取栈顶元素removeElementAt(len - 1); // 移除最后一个元素,即栈顶元素return obj;
}
pop
方法是一个同步方法,确保了线程安全。它首先获取栈顶元素(使用peek
方法),然后从Vector
中移除该元素。由于removeElementAt
方法是线程安全的,所以在多线程环境下这个操作是安全的。
查看栈顶元素 (peek
)
public synchronized E peek() {int len = size();if (len == 0)throw new EmptyStackException();return elementAt(len - 1); // 返回最后一个元素,即栈顶元素
}
peek
方法同样是线程安全的,用于获取但不移除栈顶元素。如果栈为空,则抛出EmptyStackException
。
源码解析总结
- 继承结构:
Stack
类继承自Vector
类,这意味着它自动获得了Vector
提供的线程安全特性和动态数组的管理能力。Vector 内部维护了一个可自动扩容的数组,当数组空间不足时,会自动进行扩容操作。 - 压栈 (push):实际上调用了 Vector 的 addElement 方法,该方法在数组末尾添加元素,由于栈的特性是后进先出(LIFO),因此添加在数组末尾正好模拟了这一行为。
弹栈 (pop):首先获取栈顶元素(通过 peek),然后从数组中移除这个元素。由于 Vector 的 removeElementAt 方法会改变数组的长度,从而实现了出栈操作。此方法被声明为 synchronized,确保了多线程环境下的线程安全。
查看栈顶 (peek):不改变栈的状态,仅返回栈顶元素。
- 同步操作:
Stack
类中的许多操作,特别是修改栈状态的方法(如pop
和peek
),都被声明为synchronized
,以确保在多线程环境中的线程安全性。 - 简单封装:
Stack
类主要是对Vector
类提供的方法进行了简单的封装,以符合栈数据结构的LIFO原则。比如,push
方法实质上调用了addElement
,而pop
方法则是组合了获取和移除栈顶元素的操作。
需要注意的是,虽然Stack
类提供了方便的栈操作,但在Java集合框架的现代实践中,通常推荐使用Deque
接口的实现(如ArrayDeque
)作为栈,因为它们提供了更好的性能和灵活性,而不像Stack
那样依赖于较为陈旧且同步开销较大的Vector
类。