栈是java存放内存的两种结构之一。栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线形表。
这种数据结构,它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。 也就是说,栈是只能在某一端插入和删除的特殊线性表。
我们可以将栈理解成一个杯子,或者是一个桶,而桶的底部则是实心的。这样我们存放东西的东西,就会从下到上一件件的存放,而取出东西时,只能从最上面的那个先拿。
那么这种数据结构有什么好处呢。我们举一个例子,当A方法调用B方法,B方法再调用C方法,那么他的进栈顺序就是A→B→C,而出栈的方法,也只能是C→B→A,因为A中的后续方法可能会用到B或C中的结果,所以我们必须先执行完C才能调用B的后续方法,继而调用A的后续方法。
而在java中,也在util中为我们模拟了stack这种数据结构。
public class Stack<E> extends Vector<E>
首先看类的定义,这里stack方法继承了Vector类,而Vertor是什么对象呢,打开对象看一下。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
也就是说Vertor继承了抽象的AbstractList类,同时继承了List借口,这下可以妥妥的确定,Vertor是一个List类型的数据了。也就是说是一个集合。而为什么现在Vector现在很难看到了呢,因为Vector是线程安全的,其余功能与ArrayList完全一致,这也就导致了为什么Vector使用的减少。也就是说,stack底层的结构其实是一个Collection集合。
继续往下看,就是Stack的构造方法,Stack由于其特征,我们在初始化的时候,是不好给它传入数据的。
/**
* Creates an empty Stack.
*/
public Stack() {
}
提供空参构造方法,里面隐式的调用了父类的构造方法
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
public Vector() {
this(10);
}
接着父类调用他的有参构造方法:
/**
* Constructs an empty vector with the specified initial capacity and
* with its capacity increment equal to zero.
*
* @param initialCapacity the capacity of the vector
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
可以看到这边继续往上调用了两个int参数的构造方法,这是intialCapacity为10,那么第二个int参数是什么意义呢?
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
这边super方法不用继续看,因为AbstractList中的构造方法也是空构造函数。initalCapacity就是初始化时,初始化的数组的容量,而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);
}
这边则是用来控制数组增长的方法,也就是说在stack中也可以自动增长,当然我们栈中每次增加的数量,就不受我们控制了。
也就是说,当我们调用一个new Stack()时,就会给我们生成一个内部容量为10的数组。
对于栈而言,当生成完毕之后,需要哪些方法呢,也就是,放,取,以及查看这些动作。当然其他的方法也能实现,不过对于栈而言,这些功能也不是很必要。
首先第一个方法,push方法:
public E push(E item) {
addElement(item);
return item;
}
直接调用addElement方法:
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
将计数器加1,然后确认一下数组容量是否足够,之后在相应位置也就是栈顶插入元素。
弹栈方法:
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
这边的peek方法便是查看栈顶元素,后调用removeElementAt,将len-1位置的元素,也就是最后一个(栈顶)的元素删除,达到出栈的目的。
查看栈顶元素:
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
直接返回len-1位置的元素,便也是栈顶元素。
此外还提供了empty方法来判断该栈是不是空的元素,以及search方法,查找对应元素的角标。