Java 集合:Stack(栈)

Java 中的 Stack 类(全类名 java.util.Stack)是经典的数据结构实现,严格遵循“后进先出(LIFO)” 规则,仅允许将元素压入栈顶(push() 方法),或从栈顶弹出元素(pop() 方法,读取并移除栈顶元素)。

从接口实现来看,Stack 类实际上实现了 List 接口,理论上具备列表的所有操作能力,但在实际开发中,很少会将 Stack 当作普通 List 使用 —— 仅在需要遍历、检查栈中所有存储元素时,才可能用到 List 接口的相关方法(如 get()、size() 等),如下图:

Java 集合:Stack(栈)

Java 集合:Stack(栈)

注意,Stack 类存在设计上的历史局限性:

  • 继承自 Vector 类:Stack 是遗留类 Vector 的子类,而 Vector 为保证线程安全,所有方法都添加了同步锁(synchronized)。这种强制同步会给 Stack 的每一次方法调用带来额外的性能开销,即便在单线程场景下也无法避免。

  • 依赖旧 API:Vector 依赖 Java 中已不推荐使用的旧特性(如 Enumeration 迭代器),该特性功能有限且接口设计不够灵活,早已被更强大的 Iterator 接口取代。

因此,在 Java 开发中,若需使用栈功能,更推荐用 Deque 接口替代 Stack 类(例如通过 ArrayDeque 实例化 Deque)。Deque 不仅原生支持 push()(入栈)、pop()(出栈)、peek()(查看栈顶)等栈核心操作,还能灵活选择是否需要同步,且性能更优,是更符合当前开发规范的选择。

  

Stack 栈介绍

栈(Stack)是一种遵循“后进先出(LIFO,Last-In-First-Out)”核心原则的线性数据结构,其操作逻辑类比日常生活中 “堆叠的盘子”—— 只能从最顶层放置新盘子(添加元素),也只能从最顶层拿走盘子(移除元素),不允许直接操作中间或底层的元素。

具体来说,栈的核心操作仅围绕 “顶部” 展开:

  • 入栈(Push):将新元素添加到栈的 “顶部”,成为新的栈顶元素。

  • 出栈(Pop):从栈的 “顶部” 移除并返回该元素,原栈顶的下一个元素成为新栈顶。

  • 查看栈顶(Peek):仅返回栈顶元素的值,不进行移除操作(部分场景下的常用辅助操作)。

Java 集合:Stack(栈)

  

Stack 创建

要使用 Stack,你必须首先创建一个 Stack 类的实例。例如:

Stack stack = new Stack();

  

Stack 泛型类型

你可以为 Stack 指定泛型类型,以此限定该 Stack 实例能存储的对象类型 —— 这能在编译阶段避免存入不匹配类型的元素,提升代码的安全性和可读性。例如:

// 声明一个只能存储 String 类型元素的 Stack
Stack<String> stringStack = new Stack<>();

// 声明一个只能存储 Integer 类型元素的 Stack
Stack<Integer> intStack = new Stack<>();

上面创建的 stringStack 只能包含 String 实例,intStack 只能包含 Integer 实例。

  

Stack 压入元素

当你创建好 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 弹出元素

当元素被压入 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 查看元素

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)中。

  

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 大小

你可以通过 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

  

Stack 遍历元素

要对 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());
}

增强型 for 循环

使用 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);
}

使用 Stream API

利用 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 反转列表

使用 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]

  

将 Deque 用作栈

正如本 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 文档。

  

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