栈的继承图:

栈的定义:

栈是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”类似于这种的式子,通过符号和数字的压入和弹出,可以计算这个式子,总之合理利用栈,对我们的帮助是非常大的。