栈的继承图:
栈的定义:
栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
栈的方法:
方法演示:.
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
stack.push("a");
stack.push("b");
stack.push("c");
stack.push("d");
stack.push(null);
System.out.println(stack.search("a"));
System.out.println(stack.peek());
while (!stack.empty()) {
System.out.println(stack.peek() + ".." + stack);
stack.pop();
}
Push方法:
public E push(E item) {
addElement(item);
return item;
}
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
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;
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);
}
elementCount表示里面元素的个数,elementData是保存元素的数组。先用ensureCapacityHelper判断元素的数量是否超过数组长度。当elementCount <= elementData.length时,就会把元素添加进来,如果elementCount > elementData.length,那么就会执行grow方法。这里的grow方法和其他的数组增长不一样,他不一样的一点在于我们可以用一个capacityIncrement来指示调整数组长度的时候到底增加多少。默认的情况下数组长度变为原来的两倍,如果设置了这个变量就增加指定capacityIncrement这么多的空间。栈最大的长度取决于vector里面数组能有多长。这里vector里面最大能取到Integer.MAX_VALUE。
Pop方法:
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
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 */
}
pop方法就是将栈顶的元素弹出来。如果栈里有元素,就取最顶端的那个,否则就要抛出异常。源码中,通过peek()方法取到顶端的元素,然后我们需要用removeElementAt()方法将最顶端的元素移除。
removeElementAt方法的原理是首先判断边界,是否出了边界,如果出了边界,就抛出异常,否则,将待删除元素的后面元素依次覆盖前面一个元素,直到待删除的元素被覆盖,然后长度减1,将最后一个元素的值设置为null,接着gc会回收它。
Empty方法:
public boolean empty() {
return size() == 0;
}
public synchronized int size() {
return elementCount;
}
这里的实现原理比较简单,就是看栈的计数是否为零,如果为零,就说明栈为空。同时我们可以看出来,对于栈的size函数实际上是调用的父类vector的size函数,因为有synchronized修饰,所以它是线程安全的,这也是为什么后来用ArrayList取代Vector的原因,就是因为ArrayList更快速,效率高。
Peek方法:
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
}
首先判断长度是否为零,如果为零,那么抛出一个空栈异常,否则的话就执行elementAt函数,这个函数也是Vector的函数。首先也是先判断是否是超出边界,超出就报异常,否则返回栈顶的元素。
Search方法:
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
if (o == null) {
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
search这部分就相当于找栈的匹配元素,然后返回这个元素到栈顶的距离。首先判断是否是超出边界,超出了就报异常,否则从数组的末端往前遍历,如果找到这个对象就返回。如果找不到对象,那就返回-1。
总结:
通过上面的论述,栈还是有他自己的特点的,他也有很多应用,比如在我们的编译原理的课本上给就说过,给定一个字符串表达式通过两个栈来计算这个表达式的值,比如“1+3*(3+6-1)+6”类似于这种的式子,通过符号和数字的压入和弹出,可以计算这个式子,总之合理利用栈,对我们的帮助是非常大的。