栈也是线性表,但是限定仅在表尾进行插入和删除操作的线性表;做过 Android 的应该知道,Activity 是一个典型的栈结构,它的特点就是后进先出;Stack 的源码很少,因为它的父亲 Vector 都帮它做好了,所以本篇分析主要集中在 Vector 里。
先来看两张图,对栈有个形象的认识
栈是有顺序存储结构和链式存储结构的,本篇分析的是顺序存储结构的栈,先从构造方法开始。
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected Object[] elementData;protected int elementCount;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; }
跟 ArrayList 相似,构造时需要为数组分配一个初始化的容量,这里还有一个 capacityIncrement ,即容量不够时,扩容的大小,还记得前面分析的 ArrayList 扩容的大小吗,多数情况是添加已有容量的一半,而这里 Vector 不指定默认为 0 ;
但是 capacityIncrement 为 0 不代表不扩容,而是根据需要添加的元素的大小去合理的扩容,这个合理扩容即:多数情况下时为原来的 size * 2;
添加操作 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 31 32 33 34 35 public synchronized boolean add (E e) { modCount++; ensureCapacityHelper(elementCount + 1 ); elementData[elementCount++] = e; return true ; } private void ensureCapacityHelper (int minCapacity) { if (minCapacity - elementData.length > 0 ) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;private void grow (int minCapacity) { 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 ) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
add() 方法被 synchronized 修饰了,说明这是一个同步的方法,其实 Vector 的增删改查都被 synchronized 修饰了,也证实了 Vector 是一个线程安全的集合;
后面的方法就跟 ArrayList 类似了,先做一个容量的检查,计算出需要扩大的容量大小;
如果 capacityIncrement 为 0(默认),则将容量扩大为原来的 2 倍,不为 0 则扩大设置的大小;
加需要添加的元素放在原有数组的最后一位,完成插入操作;
指定位置插入元素原理一样,多一步移位操作。
删除操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ; }
这里是指定位置的删除,将删除元素的后面元素向前进一位,元素数目减一,完成删除;
如果参数是一个对象,那么需要查找该对象在数组里的位置 index,再调用上面的方法进行删除。
修改和查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 E get (int index) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); return elementData(index); } E elementData (int index) { return (E) elementData[index]; }
实际就是普通数据的修改和查找,没有什么难度,到这里,增删改查的核心方法都贴出来了,相信大家心里都有一个疑惑,这个跟 ArrayList 很雷同嘛,为什么要用它?
其实 Vector 类的文档注释也写明了,如果不需要线程安全,那么,建议使用 ArrayList 取代 Vector。
说了半天,好像还没跟说到今天的主题,栈。当谈起栈的时候,Vector 就有意义了,栈元素的进入与移出,需要保证一定的次序,所以多线程就有可能不正常;
好,下面来看栈的方法。
栈的源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 ); }
push() 方法就是往栈顶添加元素,里面的 addElement 方法与上面分析的 add() 方法一模一样;
pop() 方法就是将栈顶的元素移除栈,里面的 removeElementAt 就是上面分析的移除方法;
peek() 方法是取出栈顶的元素,但是没有移除元素。
好,栈的分析就到这里,里面还有一些迭代器相关的集合共有的方法,就不详细分析了,还是那句,源码不难,注释都写得很清楚,希望多去读读。
Title: 基于 Vector 的栈的源码分析
Author: mjd507
Date: 2017-04-11
Last Update: 2024-01-27
Blog Link: https://mjd507.github.io/2017/04/11/Data-Structure-Stack-SourceCode/
Copyright Declaration: This station is mainly used to sort out incomprehensible knowledge. I have not fully mastered most of the content. Please refer carefully.