优秀的编程知识分享平台

网站首页 > 技术文章 正文

吃透Java集合系列五:Vector和Stack

nanyue 2024-08-12 22:29:53 技术文章 5 ℃

一:Vector分析

Vector 是线程安全的动态数组,同 ArrayList 一样继承自 AbstractList 且实现了 List、RandomAccess、Cloneable、Serializable 接口。

内部实现依然基于数组,Vector 与 ArrayList 基本是一致的,唯一不同的是 Vector 是线程安全的,会在可能出现线程安全的方法前面加上 synchronized 关键字。

其和 ArrayList 类似,随机访问速度快,插入和移除性能较差(数组原因),支持 null 元素,有顺序,元素可以重复,线程安全。

变量以及初始化:

elementData前面并没有transient关键字,但是他也实现了自定义序列化操作,前面我们了解过,如果类中自定义了readObject和writeObject,那么序列化时会调用这两个函数,如果类中不自定义readObject与writeObject,那么类在序列化的时候会调用ObjectInputStream与ObjectOutputStream中的defaultReadObject与defaultWriteObject方法进行默认序列化。

下面我们看一下Vector中自定义的readObject和writeObject

扩容

Vector 在 capacityIncrement 大于 0 时扩容 capacityIncrement 大小,否则扩容为原始容量的 2 倍,而ArrayList 在默认数组容量不够时默认扩展是 1.5 倍。

增删改查

Vector是JDK1.0时已经有的,而List框架是1.2时才出现的,所以Vector在List接口定义的增删改查以外还有他自己定义的增删改查方法,我们来看一下:

	public synchronized boolean add(E e) {
 modCount++;
 ensureCapacityHelper(elementCount + 1);
 elementData[elementCount++] = e;
 return true;
 }
	public synchronized void addElement(E obj) {
 modCount++;
 ensureCapacityHelper(elementCount + 1);
 elementData[elementCount++] = obj;
 }
 public boolean remove(Object o) {
 return removeElement(o);
 }
 public synchronized boolean removeElement(Object obj) {
 modCount++;
 int i = indexOf(obj);
 if (i >= 0) {
 removeElementAt(i);
 return true;
 }
 return false;
 }
 public synchronized E remove(int index) {
 modCount++;
 if (index >= elementCount)
 throw new ArrayIndexOutOfBoundsException(index);
 E oldValue = elementData(index);
 int numMoved = elementCount - index - 1;
 if (numMoved > 0)
 System.arraycopy(elementData, index+1, elementData, index,
 numMoved);
 elementData[--elementCount] = null; // Let gc do its work
 return oldValue;
 }
 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 */
 }
 
 
 public synchronized E set(int index, E element) {
 if (index >= elementCount)
 throw new ArrayIndexOutOfBoundsException(index);
 E oldValue = elementData(index);
 elementData[index] = element;
 return oldValue;
 }
 public synchronized void setElementAt(E obj, int index) {
 if (index >= elementCount) {
 throw new ArrayIndexOutOfBoundsException(index + " >= " +
 elementCount);
 }
 elementData[index] = obj;
 }
	public synchronized E get(int index) {
 if (index >= elementCount)
 throw new ArrayIndexOutOfBoundsException(index);
 return elementData(index);
 }
 public synchronized E elementAt(int index) {
 if (index >= elementCount) {
 throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
 }
 return elementData(index);
 }

增删改查方法基本上一样,所以Vector里面包含了大量实现相同的方法。

迭代器实现

Vector中迭代器实现和ArrayList中一样的,只不过Vector中为了保证线程安全,在方法体里面加了synchronized关键字。

	private class Itr implements Iterator<E> {
 int cursor; // index of next element to return
 int lastRet = -1; // index of last element returned; -1 if no such
 int expectedModCount = modCount;
 public boolean hasNext() {
 // Racy but within spec, since modifications are checked
 // within or after synchronization in next/previous
 return cursor != elementCount;
 }
 public E next() {
 synchronized (Vector.this) {
 checkForComodification();
 int i = cursor;
 if (i >= elementCount)
 throw new NoSuchElementException();
 cursor = i + 1;
 return elementData(lastRet = i);
 }
 }
 public void remove() {
 if (lastRet == -1)
 throw new IllegalStateException();
 synchronized (Vector.this) {
 checkForComodification();
 Vector.this.remove(lastRet);
 expectedModCount = modCount;
 }
 cursor = lastRet;
 lastRet = -1;
 }
 @Override
 public void forEachRemaining(Consumer<? super E> action) {
 Objects.requireNonNull(action);
 synchronized (Vector.this) {
 final int size = elementCount;
 int i = cursor;
 if (i >= size) {
 return;
 }
 @SuppressWarnings("unchecked")
 final E[] elementData = (E[]) Vector.this.elementData;
 if (i >= elementData.length) {
 throw new ConcurrentModificationException();
 }
 while (i != size && modCount == expectedModCount) {
 action.accept(elementData[i++]);
 }
 // update once at end of iteration to reduce heap write traffic
 cursor = i;
 lastRet = i - 1;
 checkForComodification();
 }
 }
 final void checkForComodification() {
 if (modCount != expectedModCount)
 throw new ConcurrentModificationException();
 }
 }
 /**
 * An optimized version of AbstractList.ListItr
 */
 final class ListItr extends Itr implements ListIterator<E> {
 ListItr(int index) {
 super();
 cursor = index;
 }
 public boolean hasPrevious() {
 return cursor != 0;
 }
 public int nextIndex() {
 return cursor;
 }
 public int previousIndex() {
 return cursor - 1;
 }
 public E previous() {
 synchronized (Vector.this) {
 checkForComodification();
 int i = cursor - 1;
 if (i < 0)
 throw new NoSuchElementException();
 cursor = i;
 return elementData(lastRet = i);
 }
 }
 public void set(E e) {
 if (lastRet == -1)
 throw new IllegalStateException();
 synchronized (Vector.this) {
 checkForComodification();
 Vector.this.set(lastRet, e);
 }
 }
 public void add(E e) {
 int i = cursor;
 synchronized (Vector.this) {
 checkForComodification();
 Vector.this.add(i, e);
 expectedModCount = modCount;
 }
 cursor = i + 1;
 lastRet = -1;
 }
 }

克隆实现

Vector克隆实现和ArrayList的实现一致,都是通过数组元素拷贝来实现的浅拷贝

为什么现在都不提倡使用 Vector 了?

因为 Vector 实现并发安全的原理是在每个操作方法上加锁,这些锁并不是必须要的,在实际开发中一般都是通过锁一系列的操作来实现线程安全,也就是说将需要同步的资源放一起加锁来保证线程安全,如果多个 Thread 并发执行一个已经加锁的方法,但是在该方法中又有 Vector 的存在,Vector 本身实现中已经加锁了,双重锁会造成额外的开销,即 Vector 同 ArrayList 一样有 fail-fast 问题(即无法保证遍历安全),所以在遍历 Vector 操作时又得额外加锁保证安全,还不如直接用 ArrayList 加锁性能好,所以在 JDK 1.5 之后推荐使用 java.util.concurrent 包下的并发类。此外 Vector 是一个从 JDK1.0 就有的古老集合,那时候 Java 还没有提供系统的集合框架,所以在 Vector 里提供了一些方法名很长的方法(如 addElement(Object obj),实际上这个方法和 add(Object obj) 没什么区别),从 JDK1.2 以后 Java 提供了集合框架,然后就将 Vector 改为实现 List 接口,从而导致 Vector 里有一些重复的方法。

二、Stack分析

通过继承Vector类,Stack类可以很容易的实现他本身的功能。因为大部分的功能在Vector里面已经提供支持了。

在Java中Stack类表示后进先出(LIFO)的对象堆栈。栈是一种非常常见的数据结构,它采用典型的先进后出的操作方式完成的。

Stack通过五个操作对Vector进行扩展,允许将向量视为堆栈。这个五个操作如下:

1. empty() 测试堆栈是否为空。

2. peek() 查看堆栈顶部的对象,但不从堆栈中移除它。

3. pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象。

4. push(E item) 把项压入堆栈顶部。

5. search(Object o) 返回对象在堆栈中的位置,以 1 为基数。

我们看一下源码:

public class Stack<E> extends Vector<E> {
 /**
 * Creates an empty Stack.
 */
 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;
 }
 /**
 * 查找“元素o”在栈中的位置:由栈底向栈顶方向数
 */
 public synchronized int search(Object o) {
 int i = lastIndexOf(o);
 if (i >= 0) {
 return size() - i;
 }
 return -1;
 }
}
最近发表
标签列表