为什么要把vector和stack一起来分析,因为在jdk容器的源码来说,stack是继承vector,并且代码也比较少,所以vector和stack一起来看一下。
在分析vector之前,我们先来看下vector的接口继承图:
看到这个图是不是非常熟悉,和之前分析的arraylist一模一样。arraylist的接口继承图如下:
那具体的接口分析我们就不用做了,看之前ArrayList的分析就可以了。
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { //存放元素的数组 protected Object[] elementData; //vector的长度,这里并没有叫size,但是和size功能是一样的 //可以看到size方法返回的就是这个值 protected int elementCount; //size方法 public synchronized int size() { return elementCount; } //如果想指定动态扩容时,扩容多少size,那么就设置这个值。 protected int capacityIncrement; } 复制代码
//构造方法暴露了两个参数,一个是初始容量,一个是动态扩容容量参数 public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //初始化容量 this.elementData = new Object[initialCapacity]; //指定动态扩容容量 this.capacityIncrement = capacityIncrement; } //可以不指定动态扩容容量,如果动态扩容容量为0,它会走默认的扩容逻辑。 public Vector(int initialCapacity) { this(initialCapacity, 0); } //如果初始化容量都不指定,那么初始化容量默认为10 public Vector() { this(10); } 复制代码
这里我们就分析一下add和remove方法,其他方法都比较简单,但是这里需要提的一点是,vector对父类所有的提供的方法全部加了synchronized关键字,这个关键字能够保证并发安全的,也就是常说的保证可见性,保证原子性,禁止指令重排序。但是由于这么做,vector的性能也是不如ArrayList的,由于锁粒度比较粗,实际业务中使用的非常少。
public synchronized boolean add(E e) { // 结构性修改次数增加,为了实现fast-fail的机制 modCount++; // 确认容量是否达到上限,如果达到则需要扩容 ensureCapacityHelper(elementCount + 1); // 把元素加到最后一位 elementData[elementCount++] = e; return true; } public synchronized E remove(int index) { // 结构性修改次数增加,为了实现fast-fail的机制 modCount++; // 边界检查 if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); // 获取即将remove的value用于结果返回 E oldValue = elementData(index); // 计算需要数字移动次数,为什么要计算这个,arraylist分析中介绍了线性表的删除过程。 int numMoved = elementCount - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); // 把最后一个index位置的值删除 elementData[--elementCount] = null; // Let gc do its work return oldValue; } 复制代码
// add方法 public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } // 确认容量是否达到上限 private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } // 具体扩容策略 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 如果设置了动态扩容size,每次扩容都按照capacityIncrement来扩容。 // 否则按照容量 * 2的策略扩容 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); } 复制代码
vector的代码并不复杂,我们就介绍到这里了。下面我们看一下Stack的源码,看源码之前,我们必须要了解一下数据结构-堆栈。
堆栈是一个先进后出的队列,堆栈最常见的操作就是两个,一个pop出栈操作,一个push入栈操作。
由于stack是继承Vector的,所以我们只需要看下它新增的接口部分,总共才5个方法:
// 没有调用Vector的构造器,完全创造一个空栈 public Stack() { } // push的时候,容量会自动 + 1 public E push(E item) { addElement(item); return item; } // 添加容量的具体逻辑在这里。 public synchronized void addElement(E obj) { modCount++; //每次add增加1的容量 ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; } // pop方法类似,取出栈顶元素后,容量自动减1 public synchronized E pop() { E obj; int len = size(); obj = peek(); //容量自动减1 removeElementAt(len - 1); return obj; } 复制代码
Stack源码非常简单,就分析到这里了。