C# 队列(Queue)(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在编程世界中,队列(Queue)是一种常见的数据结构,它模拟了现实生活中“先进先出”(FIFO, First-In-First-Out)的场景。无论是超市收银台前的排队、操作系统任务调度,还是网络请求的处理,队列都扮演着关键角色。本文将从基础概念出发,结合代码示例和实际场景,深入讲解 C# 队列(Queue) 的实现、特性及应用方法,帮助读者快速掌握这一核心工具。
一、队列的基本概念与类比
1.1 什么是队列?
队列是一种线性数据结构,遵循 先进先出 的原则。想象一个超市的收银台:第一位顾客进入队列,最后一位顾客离开。队列的两个核心操作是:
- Enqueue:将元素添加到队列的末尾(入队)。
- Dequeue:从队列的前端移除元素(出队)。
1.2 队列与栈的对比
队列与栈(Stack)类似,但操作方向相反:
- 栈:遵循“后进先出”(LIFO),类似叠放盘子,取最后一个放进去的。
- 队列:遵循“先进先出”,类似排队买票,先到者先被服务。
1.3 C# 中的队列实现
C# 提供了 Queue<T>
类(位于 System.Collections.Generic
命名空间),专门用于实现队列功能。此外,.NET 还提供了 ConcurrentQueue<T>
,用于多线程环境下的安全操作。
二、Queue 的核心操作与代码示例
2.1 创建与初始化
使用 Queue<T>
需要指定泛型类型,例如:
Queue<int> numberQueue = new Queue<int>();
Queue<string> stringQueue = new Queue<string>();
2.2 基础操作方法
2.2.1 Enqueue(入队)
numberQueue.Enqueue(10);
numberQueue.Enqueue(20);
// 队列内容:[10, 20]
2.2.2 Dequeue(出队)
int firstItem = numberQueue.Dequeue(); // firstItem 的值为 10
// 出队后队列内容:[20]
2.2.3 Peek(查看队首元素)
int frontItem = numberQueue.Peek(); // frontItem 的值为 20
// 队列内容未改变:[20]
2.2.4 Count 属性
int count = numberQueue.Count; // count 的值为 1
2.3 完整示例代码
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Queue<string> taskQueue = new Queue<string>();
// 入队操作
taskQueue.Enqueue("处理订单");
taskQueue.Enqueue("发送邮件");
taskQueue.Enqueue("生成报告");
Console.WriteLine("当前队列长度:" + taskQueue.Count); // 输出 3
// 出队并处理任务
while (taskQueue.Count > 0)
{
string task = taskQueue.Dequeue();
Console.WriteLine("正在处理任务:" + task);
}
}
}
三、队列的高级特性与应用场景
3.1 扩展操作:ToArray 和 CopyTo
3.1.1 ToArray
将队列内容转换为数组:
string[] tasksArray = taskQueue.ToArray();
3.1.2 CopyTo
将队列内容复制到数组的指定位置:
string[] buffer = new string[taskQueue.Count];
taskQueue.CopyTo(buffer, 0);
3.2 队列的典型应用场景
3.2.1 任务调度
在多线程环境中,队列可用于管理待执行的任务,例如:
Queue<Action> taskQueue = new Queue<Action>();
taskQueue.Enqueue(() => Console.WriteLine("任务1"));
taskQueue.Enqueue(() => Console.WriteLine("任务2"));
// 启动线程逐个执行队列中的任务
3.2.2 缓存与缓冲
在网络编程中,队列可用于缓冲接收到的数据包,避免直接处理大量数据:
Queue<byte[]> dataBuffer = new Queue<byte[]>();
// 接收数据包时 Enqueue,处理时 Dequeue
四、性能与线程安全
4.1 时间复杂度分析
操作 | 平均时间复杂度 |
---|---|
Enqueue | O(1) |
Dequeue | O(1) |
Peek | O(1) |
Contains | O(n) |
4.2 线程安全问题
Queue<T>
是非线程安全的。在多线程环境下,应使用 ConcurrentQueue<T>
:
ConcurrentQueue<int> safeQueue = new ConcurrentQueue<int>();
safeQueue.Enqueue(100); // 线程安全的入队操作
int dequeuedItem;
bool success = safeQueue.TryDequeue(out dequeuedItem); // 返回是否成功
五、队列的扩展与优化
5.1 自定义队列实现
若需自定义队列行为(例如限制容量),可通过继承 Queue<T>
或手动实现:
public class BoundedQueue<T> : Queue<T>
{
private readonly int _capacity;
public BoundedQueue(int capacity) => _capacity = capacity;
public new void Enqueue(T item)
{
if (Count >= _capacity)
throw new InvalidOperationException("队列已满");
base.Enqueue(item);
}
}
5.2 优先队列(Priority Queue)
虽然 Queue<T>
不直接支持优先级,但可通过 SortedList
或第三方库(如 System.Collections.PriorityQueue
)实现:
// 使用 SortedList 实现简单优先队列
SortedList<int, string> priorityQueue = new SortedList<int, string>();
priorityQueue.Add(1, "高优先级任务");
priorityQueue.Add(2, "中优先级任务");
六、常见问题与解决方案
6.1 为什么 Dequeue 可能抛出异常?
当队列为空时调用 Dequeue
或 Peek
,会抛出 InvalidOperationException
。解决方案包括:
if (taskQueue.Count > 0)
{
string task = taskQueue.Dequeue();
// 处理任务
}
6.2 如何遍历队列?
由于队列的 FIFO 特性,遍历通常需结合 Peek
和 Dequeue
:
while (taskQueue.Count > 0)
{
string task = taskQueue.Dequeue();
Console.WriteLine(task);
// 若需保留队列内容,需重新 Enqueue
taskQueue.Enqueue(task);
}
结论
通过本文,我们系统地学习了 C# 队列(Queue) 的核心概念、操作方法及应用场景。从基础的 Enqueue
和 Dequeue
,到多线程环境下的 ConcurrentQueue<T>
,再到优先队列的实现技巧,队列不仅是算法与数据结构的基础,更是解决实际问题的重要工具。
对于开发者而言,掌握队列的使用不仅能提升代码的效率和可维护性,还能在任务调度、缓冲处理等场景中发挥关键作用。建议读者通过实际项目中的队列应用(如消息队列、任务队列)进一步巩固知识,并探索其与栈、优先队列等结构的结合使用场景。
通过不断实践和优化,队列将成为你编程工具箱中不可或缺的利器。