Java 中的 Stack 类(全类名 java.util.Stack)是经典的栈数据结构实现,严格遵循“后进先出(LIFO)” 规则,仅允许将元素压入栈顶(push() 方法),或从栈顶弹出元素(pop() 方法,读取并移除栈顶元素)。
从接口实现来看,Stack 类实际上实现了 List 接口,理论上具备列表的所有操作能力,但在实际开发中,很少会将 Stack 当作普通 List 使用 —— 仅在需要遍历、检查栈中所有存储元素时,才可能用到 List 接口的相关方法(如 get()、size() 等),如下图:
注意,Stack 类存在设计上的历史局限性:
继承自 Vector 类:Stack 是遗留类 Vector 的子类,而 Vector 为保证线程安全,所有方法都添加了同步锁(synchronized)。这种强制同步会给 Stack 的每一次方法调用带来额外的性能开销,即便在单线程场景下也无法避免。
依赖旧 API:Vector 依赖 Java 中已不推荐使用的旧特性(如 Enumeration 迭代器),该特性功能有限且接口设计不够灵活,早已被更强大的 Iterator 接口取代。
因此,在 Java 开发中,若需使用栈功能,更推荐用 Deque 接口替代 Stack 类(例如通过 ArrayDeque 实例化 Deque)。Deque 不仅原生支持 push()(入栈)、pop()(出栈)、peek()(查看栈顶)等栈核心操作,还能灵活选择是否需要同步,且性能更优,是更符合当前开发规范的选择。
栈(Stack)是一种遵循“后进先出(LIFO,Last-In-First-Out)”核心原则的线性数据结构,其操作逻辑类比日常生活中 “堆叠的盘子”—— 只能从最顶层放置新盘子(添加元素),也只能从最顶层拿走盘子(移除元素),不允许直接操作中间或底层的元素。
具体来说,栈的核心操作仅围绕 “顶部” 展开:
入栈(Push):将新元素添加到栈的 “顶部”,成为新的栈顶元素。
出栈(Pop):从栈的 “顶部” 移除并返回该元素,原栈顶的下一个元素成为新栈顶。
查看栈顶(Peek):仅返回栈顶元素的值,不进行移除操作(部分场景下的常用辅助操作)。
要使用 Stack,你必须首先创建一个 Stack 类的实例。例如:
Stack stack = new Stack();
你可以为 Stack 指定泛型类型,以此限定该 Stack 实例能存储的对象类型 —— 这能在编译阶段避免存入不匹配类型的元素,提升代码的安全性和可读性。例如:
// 声明一个只能存储 String 类型元素的 Stack Stack<String> stringStack = new Stack<>(); // 声明一个只能存储 Integer 类型元素的 Stack Stack<Integer> intStack = new Stack<>();
上面创建的 stringStack 只能包含 String 实例,intStack 只能包含 Integer 实例。
当你创建好 Stack 实例后,即可将元素推至栈顶。需要注意的是,Stack 中存储的元素必须是 Java 对象(基本数据类型需通过对应的包装类,如 int 需用 Integer、char 需用 Character 等转换后才能存入),因此本质上是将对象推入栈中。
可以调用 Stack 的 push() 方法,将传入的对象作为新元素压入栈顶,并成为当前栈的顶层元素。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); // 栈顶 System.out.println(stack); //[one, two]
当元素被压入 Stack 后,可通过弹出操作(pop)将其从 Stack 中取出。一旦执行弹出,该元素会被彻底从 Stack 中移除,此时栈顶元素自动变为之前压入的那个元素(即被弹出元素的前一个元素)。
弹出元素需调用 pop() 方法,该方法会返回被弹出的元素,同时完成栈顶元素的更新。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] String pop = stack.pop(); // 弹出栈顶元素 System.out.println(pop); // two System.out.println(stack); //[one]
Stack 类中提供了一个名为 peek () 的方法,该方法可用于查看栈顶元素,且不会将此元素从栈中弹出。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] String peek = stack.peek(); //查看栈顶元素 System.out.println(peek); // two System.out.println(stack); //[one, two]
运行示例后,peek 变量将包含字符串对象“two”,该对象是在调用 peek() 之前被压入栈(Stack)的。调用 peek() 之后,这个字符串对象仍然存在于栈(Stack)中。
你可以通过 search () 方法在栈中搜索特定对象并获取其索引。搜索过程中,栈内每个对象都会调用 equals () 方法来判断被搜索对象是否存在,返回的索引从栈顶开始计数,其中栈顶元素的索引为 1。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] int pos1 = stack.search("one"); System.out.println(pos1); //2 int pos2 = stack.search("two"); System.out.println(pos2); //1
你可以通过 Stack 的 size() 方法获取栈的大小,即当前存储在栈中的元素数量。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] int size = stack.size(); System.out.println(size); //2
要对 Java 栈的元素进行遍历,有以下几种方式:
首先需要获取其 Java 迭代器,而获取该迭代器的核心步骤就是调用栈提供的 iterator () 方法。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] Iterator<String> iterator = stack.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }
使用 Java 中的增强 for 循环也可以遍历 Java 栈,例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] for(String val : stack) { System.out.println(val); }
利用 Java Stream API 同样能处理 Java 栈上的元素。具体做法是先由栈调用 stream () 方法得到一个流,一旦获取了这个流(Stream),便可以着手处理流中的元素。例如:
Stack<String> stack = new Stack<String>(); stack.push("one"); stack.push("two"); System.out.println(stack); //[one, two] stack.stream().forEach(e -> { System.out.println(e); });
注意,此示例使用 Java Lambda 表达式作为 Stream.forEach() 方法的参数。该 lambda 只是将元素打印到 System.out。
使用 Stack 能实现 List 的反转,步骤如下:先从列表索引为 0 的元素开始,依次将各元素压入栈中(索引 1 的元素紧随其后,以此类推),且每个元素都会先从列表中移除再入栈;待所有元素入栈后,再将它们逐个弹出并添加回空列表,便完成了反转操作。例如:
List<String> list = new ArrayList<>(); list.add("one"); list.add("two"); list.add("three"); System.out.println(list); //[one, two, three] Stack<String> stack = new Stack<String>(); while (list.size() > 0) { stack.push(list.remove(0)); } while (stack.size() > 0) { list.add(stack.pop()); } System.out.println(list); //[three, two, one]
正如本 Java 教程顶部所提到的,你也可以将 Java Deque 用作栈。例如:
Deque<String> dequeAsStack = new ArrayDeque<String>(); dequeAsStack.push("one"); dequeAsStack.push("two"); dequeAsStack.push("three"); System.out.println(dequeAsStack); //[three, two, one] String one = dequeAsStack.pop(); System.out.println(one); //three String two = dequeAsStack.pop(); System.out.println(two); //two String three = dequeAsStack.pop(); System.out.println(three); //one
如你所见,其使用方式和常规 Stack 相比,并没有明显差别,整体十分相似。
更多信息查看 https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html API 文档。