Java 集合:Stack 详解

在 Java 集合框架中,Stack 是一种基于后进先出(LIFO, Last-In-First-Out) 原则的线性数据结构,主要用于实现栈的相关操作。

栈结构如下图:

Java 集合:Stack 详解

 

Stack 基本特性

注意,Stack 并非独立设计,而是直接继承 Vector(Vector 是 Java 早期的动态数组实现类),本质上是通过 Vector 的动态数组机制存储元素。这意味着 Stack 天然具备 Vector 的特性,例如:动态扩容(数组满时自动增长)、支持通过索引访问元素等,但也继承了 Vector 的设计局限性(如暴露非栈操作方法)。

Stack 类支持元素的压栈(push)、出栈(pop)、查看栈顶元素(peek)等操作。

由于 Stack 直接继承自 Vector,因此底层是基于数组实现,本质上是通过动态数组来存储元素。

  

Stack 优缺点

优点

  • 线程安全:由于继承自 Vector,所有方法都具有同步特性,适合多线程环境。

  • 操作简单:提供了直观的栈操作方法,易于理解和使用。

缺点

  • 性能问题:同步操作会带来额外的性能开销,在单线程环境中效率较低。

  • 设计缺陷:继承 Vector 导致 Stack 暴露了所有 Vector 的方法(如 add(int, E)),破坏了栈的封装性(理论上栈只能从顶端操作)。

注意,由于 Stack 存在性能问题和设计缺陷,在实际开发中,推荐使用 Deque 接口的实现类(如 ArrayDeque)替代 Stack,原因如下:

  • Deque 提供了更规范的栈操作方法(push()、pop()、peek())。

  • ArrayDeque 是非同步的,性能优于 Stack。

  • 封装性更好,不会暴露额外的操作方法。

例如:

import java.util.ArrayDeque;
import java.util.Deque;

public class DequeExample {
    
    public static void main(String[] args) {
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(10);
        stack.push(20);
        System.out.println(stack.pop()); // 输出: 20
    }
    
}

  

Stack 核心方法

Stack 类的主要方法如下:

  • push(E item)  将元素压入栈顶,返回被压入的元素。

  • pop()  移除并返回栈顶元素,栈为空时抛出 EmptyStackException。

  • peek()  返回栈顶元素(不移除),栈为空时抛出 EmptyStackException。

  • empty()  判断栈是否为空,为空返回 true 无。

  • search(Object(Object(int o)  返回指定元素在栈中的位置(从栈顶开始计数),元素不存在时返回 -1。

  

Stack 基础用法

下面通过一个示例介绍 Stack 类的基础用法,如压栈、出栈等操作,如下:

import java.util.Stack;

public class StackExample {
    
    public static void main(String[] args) {
        // 创建栈对象
        Stack<Integer> stack = new Stack<>();
        
        // 压栈操作
        stack.push(10);
        stack.push(20);
        stack.push(30);
        System.out.println("栈元素: " + stack); // 输出: [10, 20, 30]
        
        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek()); // 输出: 30
        
        // 出栈操作
        int top = stack.pop();
        System.out.println("出栈元素: " + top); // 输出: 30
        System.out.println("出栈后栈元素: " + stack); // 输出: [10, 20]
        
        // 判断栈是否为空
        System.out.println("栈是否为空: " + stack.empty()); // 输出: false
        
        // 查找元素位置
        int index = stack.search(10);
        System.out.println("元素10的位置: " + index); // 输出: 2(从栈顶开始计数)
    }
    
}

运行结果:

栈元素: [10, 20, 30]
栈顶元素: 30
出栈元素: 30
出栈后栈元素: [10, 20]
栈是否为空: false
元素10的位置: 2

  

Stack 原理分析

下面通过分析 Stack 源码,了解 Stack 的实现原理:

package java.util;

public class Stack<E> extends Vector<E> {
    /** 用于JDK 1.0.2版本的序列化兼容性 */
    @java.io.Serial
    private static final long serialVersionUID = 1224463164541339165L;
    
    /**
     * 创建一个空栈
     */
    public Stack() {
    }

    /**
     * 将元素压入栈顶。此操作与以下代码效果完全相同:
     * addElement(item)
     *
     * @param item  要压入栈的元素
     * @return 被压入的元素(即参数 {@code item})
     * @see java.util.Vector#addElement
     */
    public E push(E item) {
        // 调用父类 Vector 的 addElement 方法,将元素添加到末尾(栈顶)
        addElement(item);
        return item;
    }

    /**
     * 移除栈顶的对象,并将该对象作为此方法的返回值
     *
     * @return  栈顶的对象(即Vector对象的最后一个元素)
     * @throws  EmptyStackException  如果栈为空时调用此方法
     */
    public synchronized E pop() {
        E obj;
        int len = size();  // 获取当前栈的长度

        obj = peek();  // 先获取栈顶元素(会检查栈是否为空)
        removeElementAt(len - 1);  // 移除栈顶元素(Vector的最后一个元素)
        return obj;
    }

    /**
     * 查看栈顶的对象,但不从栈中移除它
     *
     * @return  栈顶的对象(即Vector对象的最后一个元素)
     * @throws  EmptyStackException  如果栈为空时调用此方法
     */
    public synchronized E peek() {
        int len = size();  // 获取当前栈的长度
        if (len == 0)
            throw new EmptyStackException();  // 栈为空时抛出异常
        return elementAt(len - 1);  // 返回Vector的最后一个元素(栈顶)
    }

    /**
     * 测试此栈是否为空
     *
     * @return  当且仅当栈不包含任何元素时返回 true;否则返回 false;
     */
    public boolean empty() {
        return size() == 0;  // 通过判断 Vector 的大小是否为 0 来确定栈是否为空
    }

    /**
     * 返回对象在栈中的 1-based(从1开始)位置。
     * 如果对象 o 是栈中的元素,此方法返回该元素距离栈顶的距离。
     * 栈顶元素的距离被视为 1。此方法使用 equals 方法来比较 o 与栈中的元素。
     *
     * @param o  要查找的对象
     * @return  从栈顶开始计算的 1-based 位置;返回值 -1 表示该对象不在栈中
     */
    public synchronized int search(Object o) {
        // 调用 Vector 的 lastIndexOf 方法,获取对象在向量中最后一次出现的索引(从0开始)
        int i = lastIndexOf(o);
        if (i >= 0) {
            // 转换为 1-based 的栈顶距离(栈顶为1)
            return size() - i;
        }
        return -1;  // 未找到对象
    }
}

更多信息请参考 https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html API 文档。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号