应优先选用arraydeque而非linkedlist:arraydeque基于数组、性能更优、gc压力小,仅在需null元素时才用linkedlist;禁用priorityqueue以防乱序;offer()比add()更安全;poll()返回null仅表示瞬时为空,不可轮询;offerfirst()可插队但非queue接口方法。

用 LinkedList 还是 ArrayDeque?别选错
Java 里能当队列用的集合不少,但真正适合叫号系统的只有两个:LinkedList 和 ArrayDeque。前者常被误认为“天然队列”,其实它底层是双向链表,每次 poll() 虽然 O(1),但内存开销大、缓存不友好;后者才是 JDK 推荐的栈/队列实现——数组结构、无同步、扩容策略合理,吞吐更高。
- 日常叫号(百人级以内)直接用
ArrayDeque,性能稳、GC 压力小 - 如果必须支持 null 元素(比如用
null表示“暂停叫号”),ArrayDeque不允许,得切回LinkedList - 千万别用
PriorityQueue——它按优先级排序,不是先进先出,叫号乱序就是它干的
offer() 和 add() 在叫号时行为完全不同
往队列加号时,表面看都能塞进新号,但失败处理逻辑天差地别。叫号系统要稳定,就不能让一次异常导致整个流程卡死。
-
offer()失败返回false(比如队列满,虽然ArrayDeque默认无界,但自定义容量时可能触发),可安全判断并记录日志 -
add()失败直接抛IllegalStateException,没 catch 就崩,线上叫号中断没人兜底 - 真实场景建议封装一层:
if (!queue.offer(newNumber)) { log.warn("叫号队列已满,丢弃号 {}" , newNumber); }
多线程取号时,poll() 返回 null 的真实含义
柜台窗口调用 poll() 取下一个号,结果拿到 null,第一反应是“队列空了”,但实际可能是并发问题或误判状态。
-
poll()返回null只代表此刻队列为空,不代表“永远没号了”——可能前一秒刚被其他窗口取走,后一秒就有新号进队 - 不要写
while (queue.poll() == null) { Thread.sleep(100); }这种轮询,CPU 白耗、响应延迟高 - 更合理的做法是结合业务节奏:比如每 3 秒查一次,或用简单的状态标记(
AtomicBoolean hasNewNumber)配合wait/notify,但注意ArrayDeque本身不提供阻塞能力
号码重入队的坑:offerFirst() 不是“插队”,而是破坏顺序
有些业务需要临时插队(比如老人优待号),但 Java 集合里没有“在第 N 位插入”的标准操作,容易误用。
立即学习“Java免费学习笔记(深入)”;
-
offerFirst()是把号加到队头,下一次poll()就会立刻取到它——这确实是插队,但只适用于Deque实现(ArrayDeque支持,LinkedList也支持),Queue接口不定义这个方法 - 如果用
addAll(0, list)想批量插队,ArrayDeque不支持随机索引操作,会抛UnsupportedOperationException - 真要支持复杂插队规则(如“排在所有 60 岁以上号之后”),别硬靠集合排序,改用带优先级的业务逻辑 + 时间戳字段,再按需生成新队列
队列看着简单,但叫号系统里每个 poll() 和 offer() 调用背后,都连着用户等待时间和柜台工作节奏——参数选错、异常没兜住、null 判定模糊,都会让“下一个请到 X 号”这句话变得不可信。










