Queue 初始化和基本操作
直接用
new Queue<t>()</t>创建泛型队列,这是最安全、最常用的方式。非泛型
Queue已过时,不推荐在新代码中使用,它会引发装箱/拆箱开销且缺乏类型安全。
入队用
Enqueue(),出队用
Dequeue(),查看队首用
Peek()(不移除)。注意
Dequeue()在队列为空时抛出
InvalidOperationException,不是返回
null或默认值。
var q = new Queue<string>();
q.Enqueue("first");
q.Enqueue("second");
Console.WriteLine(q.Peek()); // 输出 "first"
Console.WriteLine(q.Dequeue()); // 输出 "first",队列剩 ["second"]判断是否为空和遍历队列
Count属性返回当前元素数量,
Count == 0是判断空队列的可靠方式;别用
Peek()+ 异常捕获来“试探”,这属于反模式。
遍历时不能边遍历边
Dequeue()(除非你明确要清空),否则会跳过元素或抛异常。需要只读遍历就用
foreach;需要逐个处理并移除,用
while (q.Count > 0)循环更清晰:
while (q.Count > 0)
{
var item = q.Dequeue();
Process(item);
}线程安全问题:Queue 不是线程安全的
多个线程同时调用
Enqueue()或
Dequeue()可能导致数据错乱或异常,.NET 没有内置锁机制。不要自己加
lock包裹每次操作——性能差且易出错。
正确做法是:
- 短期方案:改用
ConcurrentQueue<t></t>,它提供无锁的
TryEnqueue()和
TryDequeue(),返回
bool表示是否成功
- 长期方案:评估是否真需要多线程共享队列,有时用生产者-消费者模式 +
BlockingCollection<t></t>更合适
常见错误:把 Queue 当 Stack 用
有人误以为
Peek()+
Dequeue()能实现后进先出,但 Queue 是 FIFO,最后入队的永远在队尾。如果需要 LIFO 行为,直接换用
Stack<t></t>。
另一个典型错误是反复调用
Peek()以为能“预读多个元素”——它只返回队首,不会移动内部指针。想看前 N 个?只能复制一份再遍历,或者改用
List<t></t>并手动维护索引。
队列容量不是硬限制,
Queue<t></t>内部是循环数组,自动扩容;但频繁扩容会影响性能,如果预估大小较稳定,初始化时传入容量参数更高效:
new Queue<int>(1024)</int>。
