杰瑞科技汇

栈和队列的区别?

它们最核心的区别在于 数据的存取顺序

栈和队列的区别?-图1
(图片来源网络,侵删)
  • :遵循 后进先出 的原则,就像一摞盘子,你最后放上去的盘子,会最先被拿走。
  • 队列:遵循 先进先出 的原则,就像排队买票,排在最前面的人最先买到票,离开队伍。

下面我们从多个维度进行详细的对比。


核心对比表格

特性 队列
数据存取顺序 LIFO (Last-In, First-Out)
后进先出
FIFO (First-In, First-Out)
先进先出
主要操作 push (入栈), pop (出栈) enqueue (入队), dequeue (出队)
操作位置 所有操作都在 同一端 进行(称为栈顶) 操作在 两端 进行(一端入队,一端出队)
类比现实 一摞盘子、子弹夹、函数调用栈 排队买票、打印任务队列
应用场景 括号匹配、表达式求值、函数调用、撤销操作 消息队列、任务调度、广度优先搜索
Java 实现方式 java.util.Stack (已过时,不推荐)
java.util.LinkedList
java.util.ArrayDeque (推荐)
java.util.LinkedList
java.util.ArrayDeque (推荐)
java.util.PriorityQueue (优先级队列)

详细解释

数据存取顺序

这是两者最根本的区别。

    • 后进先出:想象一个只能从顶部开口的箱子,你最后放进去的物品,会最先被拿出来。
    • 操作端:只有一个操作端,称为 栈顶,所有的添加和删除操作都只能在栈顶完成。
  • 队列

    栈和队列的区别?-图2
    (图片来源网络,侵删)
    • 先进先出:想象一个只有一个入口和一个出口的管道,你从入口放进去的球,会按照放入的顺序从出口滚出来。
    • 操作端:有两个操作端,一端称为 队尾,用于添加元素(入队);另一端称为 队头,用于移除元素(出队)。

主要操作

数据结构 添加元素 移除元素 查看顶端/队首元素
push(element) pop() peek()
队列 offer(element) / add(element) poll() / remove() peek()
  • push / pop / peek (Stack)

    • push(E e): 将元素压入栈顶。
    • pop(): 弹出并返回栈顶的元素,如果栈为空,会抛出 EmptyStackException
    • peek(): 查看栈顶的元素,但不移除它,如果栈为空,会抛出 EmptyStackException
  • offer / poll / peek (Queue)

    • offer(E e): 将元素添加到队尾,如果成功,返回 true;如果队列已满(对于有容量限制的队列),返回 false,这是推荐使用的安全方法。
    • poll(): 移除并返回队头的元素,如果队列为空,返回 null,这是推荐使用的安全方法。
    • peek(): 查看队头的元素,但不移除它,如果队列为空,返回 null

注意Queue 接口也提供了 add(), remove(), element() 方法,它们的功能与 offer(), poll(), peek() 类似,但在操作失败时会抛出异常而不是返回 null

Java 中的实现方式

在 Java 中,栈和队列都不是独立的类,它们是接口,我们需要通过具体的类来实现它们。

栈和队列的区别?-图3
(图片来源网络,侵删)

如何实现一个栈?

不推荐java.util.Stack

  • 这是一个古老的类,继承自 Vector
  • 它是 线程同步 的,这意味着有性能开销。
  • 它暴露了很多 Vector 的方法(如 get(), set()),破坏了栈“只操作栈顶”的原则,不安全。
  • 除非有特殊需求,否则应避免使用 java.util.Stack

推荐java.util.ArrayDeque

  • ArrayDeque 是一个基于数组的双端队列实现,性能非常出色。
  • 它实现了 Deque 接口,而 Deque (Double-Ended Queue) 接口扩展了 Queue 接口,并提供了 push()pop() 方法来模拟栈的行为。
  • 优点:性能高(无锁,非线程同步)、内存效率高。

示例代码:

import java.util.ArrayDeque;
import java.util.Deque; // Deque 是双端队列接口
public class StackExample {
    public static void main(String[] args) {
        // 使用 ArrayDeque 作为栈
        Deque<String> stack = new ArrayDeque<>();
        // push 操作 (入栈)
        stack.push("Apple");
        stack.push("Banana");
        stack.push("Cherry");
        System.out.println("Stack: " + stack); // 输出: [Cherry, Banana, Apple]
        // peek 操作 (查看栈顶)
        String top = stack.peek();
        System.out.println("Top element: " + top); // 输出: Cherry
        // pop 操作 (出栈)
        String popped = stack.pop();
        System.out.println("Popped element: " + popped); // 输出: Cherry
        System.out.println("Stack after pop: " + stack); // 输出: [Banana, Apple]
    }
}

如何实现一个队列?

推荐java.util.ArrayDeque

  • 和用作栈一样,ArrayDeque 也是实现队列的首选。
  • 它提供了 offer() (入队), poll() (出队), peek() (查看队首) 等方法。
  • 优点:性能高,是大多数场景下的默认选择。

备选java.util.LinkedList

  • LinkedList 实现了 ListDeque 接口,因此它天生就可以作为队列使用。
  • 当队列中元素数量变化很大,或者需要频繁在中间插入/删除时,LinkedList 可能比 ArrayDeque 表现更好。
  • 缺点:相比 ArrayDeque,它的内存占用稍高,且性能不如 ArrayDeque 稳定。

示例代码:

import java.util.ArrayDeque;
import java.util.Queue;
public class QueueExample {
    public static void main(String[] args) {
        // 使用 ArrayDeque 作为队列
        Queue<String> queue = new ArrayDeque<>();
        // offer 操作 (入队)
        queue.offer("Person 1");
        queue.offer("Person 2");
        queue.offer("Person 3");
        System.out.println("Queue: " + queue); // 输出: [Person 1, Person 2, Person 3]
        // peek 操作 (查看队首)
        String front = queue.peek();
        System.out.println("Front element: " + front); // 输出: Person 1
        // poll 操作 (出队)
        String served = queue.poll();
        System.out.println("Served element: " + served); // 输出: Person 1
        System.out.println("Queue after poll: " + queue); // 输出: [Person 2, Person 3]
    }
}

应用场景举例

栈 的应用场景

  1. 函数调用栈:当你调用一个函数时,它的返回地址、参数和局部变量会被压入调用栈,当函数返回时,这些信息会从栈中弹出,这是程序执行的基础。
  2. 括号匹配:在编译器或代码编辑器中,检查代码中的括号 , [], 是否匹配,遇到左括号就压入栈,遇到右括号就弹出栈顶元素进行匹配。
  3. 表达式求值:在计算逆波兰表达式(后缀表达式)时,使用栈来存储操作数。
  4. 撤销/重做:文本编辑器的“撤销”功能通常用一个栈来保存用户的历史操作。

队列 的应用场景

  1. 消息队列:在分布式系统中,用于异步处理任务,一个服务将任务放入队列,另一个或多个工作服务从队列中取出任务来执行,实现了系统解耦和削峰填谷。
  2. 任务调度:操作系统中的进程调度、线程池中的任务管理,都使用队列来保证任务按顺序执行。
  3. 广度优先搜索:在图的遍历算法中,使用队列来存储待访问的节点,保证先访问离起点近的节点。
  4. 打印任务:打印机会将打印任务放入队列,按照先来后到的顺序依次打印。

队列
核心思想 后进先出 先进先出
操作口诀 后进先出,像一个死胡同 先进先出,像一条单行道
Java 推荐实现 ArrayDeque (作为 Deque 使用) ArrayDeque
选择依据 当你需要处理“或“最新”的数据,并且需要快速获取和移除它时。 当你需要处理数据,并且必须按照它们到达的顺序进行处理时。

记住这个核心区别,你就能在合适的场景下选择正确的数据结构。

分享:
扫描分享到社交APP
上一篇
下一篇