“小米,你面试那家十八线大厂的 JVM 题都问了啥?”
“栈和队列!你猜我怎么答的?我直接讲了个日常故事,面试官听完都笑了——然后就让我进二面了!”
社招面试现场,小米又被问到“老问题”
事情发生在不久前的某次 JVM 社招面试中。作为一个写 Java 写了快十年的开发者,我对 JVM 这块多少还是有点底气的。可没想到,刚坐下,面试官的第一个问题却不是问 GC,也不是类加载,而是:
“你能说说什么是栈和队列吗?它们在 JVM 里有什么应用,区别又在哪里?”
你是不是也觉得这题太基础了?但我告诉你——越基础的题目,越能看出候选人的基本功和表达能力。
我当时没有急着丢概念,而是笑着说:
“那我就给您讲个故事吧,栈和队列的故事,就像生活中我们排队买奶茶和叠衣服的过程一样。”
从生活场景讲起:奶茶店和衣柜
故事一:排队买奶茶——这是“队列”Queue
想象一下你中午和同事去奶茶店,人太多了,你只能排队。
每来一个人,都排在你后面;而奶茶店做完一个奶茶,是先给最前面那位顾客,对吧?这就是典型的先进先出(FIFO) 的规则:
- 谁先来,谁先拿;
- 后来的人只能等前面的人拿完;
- 加塞是禁止的(当然现实可能会有,但数据结构中不允许)。
所以,我们可以说:“排队买奶茶”,就是队列 Queue的生活版写照!
故事二:叠衣服进衣柜——这是“栈”Stack
再来看另一个场景:你周末打扫卫生,把衣服一件件折好往柜子里叠。
每一件新衣服,都放在上一件的上面;而你下次想拿衣服,肯定是从最上面拿,对吧?也就是最后放进去的,最先拿出来。
这就是后进先出(LIFO) 的规则。
- 谁最后放进去,谁最先拿出来;
- 如果你想拿最下面的那件衣服,是不是还得先拿出上面所有的衣服?这很麻烦,但这就是栈的特性。
所以我们说,叠衣服进柜子,就是栈 Stack的生活版写照!
从生活回到技术:栈和队列的定义与区别
故事讲完了,面试官已经笑出了声。我乘胜追击,马上补充了技术定义:
栈(Stack)
- 定义:一种受限的线性表,只允许在一端进行插入和删除操作。
- 操作方向:只能在“栈顶”操作。
- 顺序特性:后进先出(LIFO)。
- 常用操作:
-
- push():压栈
-
- pop():出栈
-
- peek():查看栈顶元素但不移除
队列(Queue)
- 定义:一种受限的线性表,只允许在一端插入、另一端删除。
- 操作方向:插入在“队尾”,删除在“队头”。
- 顺序特性:先进先出(FIFO)。
- 常用操作:
-
- offer() / add():入队
-
- poll() / remove():出队
-
- peek() / element():查看队头元素
栈 vs 队列 核心区别
JVM 中的栈和队列:不仅仅是数据结构
聊完概念,我补了一句:“其实 JVM 里,‘栈’和‘队列’都无处不在!”
面试官点点头,于是我开始举例:
1. 栈在 JVM 中的应用:Java 虚拟机栈
JVM 中的每个线程都会分配一个 虚拟机栈(Java Virtual Machine Stack) ,这个栈里保存的是:
- 栈帧(Stack Frame) :代表一个方法的调用;
- 每个栈帧包括局部变量表、操作数栈、方法返回地址等。
每次方法调用,就会压入栈帧;方法执行结束,就会弹出栈帧。
是不是就像我们上面说的“叠衣服”?调用链越深,衣服堆得越高;异常时如果不处理,直接堆栈溢出,就是“StackOverflowError”。
2. 队列在 JVM 中的应用:线程池任务调度
再说说队列的使用,最典型的就是线程池:
线程池中的任务,不可能同时执行,怎么办?就用阻塞队列(如 LinkedBlockingQueue) 来排队!
比如你提交了 10 个任务,但只有 5 个线程,前 5 个任务会立即执行,后 5 个会排在队列中等待调度。
队列的设计让线程池调度更高效,也避免了线程频繁创建销毁。
面试官继续追问:“那你用过哪些实现类?”
这个问题就涉及 Java 里提供的数据结构了,我也提前准备过,直接开讲:
栈的实现类:
- Stack:古老的类,继承 Vector,线程安全,但已不推荐;
- 推荐使用:Deque 接口的 ArrayDeque 作为栈的替代方案,效率更高。
队列的实现类:
- LinkedList:实现了 Queue 接口,可作为普通队列;
- PriorityQueue:优先队列,元素会按优先级排列;
- ArrayDeque:双端队列,高效无锁;
- ConcurrentLinkedQueue:并发无锁队列,适合多线程场景;
- BlockingQueue(接口):
-
- 实现类如 LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue 等;
-
- 支持线程阻塞等待入队或出队,广泛用于线程池。
面试官最后一问:如果你来设计一个栈,怎么实现?
我一笑,说:“我就拿数组模拟一个栈,每次 push 就往后放,pop 就返回最后一个元素,当然还得处理溢出和动态扩容。”
并简单描述了代码逻辑,用 ArrayList 或 LinkedList 都可以轻松实现。
面试官点头:“你讲得很有条理,故事化地解释也挺好,二面通知等 HR 吧。”
我心里暗自窃喜:讲技术,不一定非得干巴巴。讲故事、类比生活,有时更打动人。
总结:栈和队列,基础但不简单
写到这里,咱们来回顾一下:
- 栈:后进先出(叠衣服)
- 队列:先进先出(买奶茶)
- JVM 中栈用于方法调用,队列用于线程调度;
- 面试不仅考你会不会,还考你“讲不讲得清”!
最后的小米叮咛
“基础不牢,地动山摇。”
栈和队列是最基础的数据结构,但千万别轻视它。你能把它讲清楚、讲有趣,才能让面试官眼前一亮。
你也可以试试讲讲:
- 浏览器的“后退”按钮是怎么用栈实现的?
- Kafka、消息队列中的“消费逻辑”是不是也是队列?
- Spring 的责任链模式用的是哪种结构?
END
欢迎评论区留言,一起交流!你还遇到过哪些看似简单却被问懵的面试题?
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!