isEmpty() 是 O(1) 操作,直接检查 head 是否为 null;而 size() 是 O(n) 遍历操作,需逐个统计节点且结果不准确,性能差距随元素增多急剧扩大,判空必须用 isEmpty()。

因为 isEmpty() 是常量时间操作,而 size() 在 ConcurrentLinkedQueue 中是 O(n) 遍历操作。
isEmpty() 只检查头节点是否为空
ConcurrentLinkedQueue 的 isEmpty() 方法直接调用 first() == null,而 first() 仅通过 volatile 读取 head 节点并返回其 item 字段(若 head 未初始化或已出队则为 null)。整个过程不遍历、无锁、无副作用,时间复杂度稳定为 O(1)。
size() 必须遍历整个链表
ConcurrentLinkedQueue 的 size() 方法无法维护精确计数(为避免写竞争牺牲一致性),只能通过从 head 开始逐个跳 next 指针统计有效节点。链表越长,耗时越久;且遍历时可能遇到正在被并发修改的中间状态,导致结果不准确甚至隐式失败(如漏计、多计或遍历中断)。
实际性能差距显著
- 元素数 10 时,size() 已比 isEmpty() 慢约 10 倍
- 元素数 10 万时,size() 可能慢上百倍甚至更多
- 在循环条件中写
while (queue.size() > 0)不仅低效,还可能漏数据——因 size() 返回的是某一瞬快照,而 poll() 执行时队列早已变化
别把 size() 当判空逻辑用
即使业务看起来“只是判断是否为空”,只要用了 size() == 0,就等于主动引入了不必要的遍历开销和不确定性。JDK 官方注释明确提醒:“Beware that, unlike in most collections, this method is NOT a constant-time operation.” 判空场景下,isEmpty() 更安全、更清晰、更快。
立即学习“Java免费学习笔记(深入)”;


















