基于单链表实现的自定义队列达特定规模时抛出StackOverflowException
嘿,这个问题我太熟了!我敢打赌你的Length属性实现是用递归遍历节点来计数的,对吧?
问题根源
CLR的调用栈是有大小限制的(默认一般是1MB左右),每次递归调用都会在栈上压入一个栈帧。当你的队列节点数达到9300时,递归的深度直接超过了栈能承受的上限,自然就抛出StackOverflowException;而9200刚好卡在栈的临界值以内,所以暂时没问题。
解决方案
别用递归了,换两种更靠谱的方式:
1. 维护计数器字段(最优方案)
在队列类里加一个私有计数器,每次入队/出队时更新它,Length属性直接返回这个值——不仅不会栈溢出,还是O(1)的时间复杂度。
示例代码:
public class MyQueue<T> { private MyQueueNode<T> _head; private MyQueueNode<T> _tail; private int _count; // 核心计数器 public int Length => _count; public void Enqueue(T item) { var newNode = new MyQueueNode<T>(item); if (_tail == null) { _head = newNode; _tail = newNode; } else { _tail.Next = newNode; _tail = newNode; } _count++; // 入队加1 } public T Dequeue() { if (_head == null) throw new InvalidOperationException("队列已空"); var item = _head.Value; _head = _head.Next; if (_head == null) _tail = null; _count--; // 出队减1 return item; } }
2. 用迭代代替递归(应急方案)
如果实在不想改结构,就把递归遍历改成while循环迭代——这样不会占用调用栈空间,多少节点都不怕。
示例代码:
public int Length { get { int count = 0; var currentNode = _head; while (currentNode != null) { count++; currentNode = currentNode.Next; } return count; } }
总结
递归写起来简洁,但在这种可能有大量节点的场景里绝对是雷区。维护计数器才是队列长度的正确打开方式,既高效又安全。
内容的提问来源于stack exchange,提问作者Lee




