Queue<T>とは何か? FIFO(先入れ先出し)
C#のSystem.Collections.Generic名前空間には、List<T>(動的配列)やStack<T>(スタック)と並び、Queue<T>(キュー)という特定の操作に特化したコレクションが用意されています。
Queue<T>(キュー)は、「FIFO (First-In, First-Out)=先入れ先出し」というデータ構造を実装したものです。
これは、現実世界の「窓口の行列」や「レジの順番待ち」に例えられます。
- 新しい人(データ)は、常に行列の**一番後ろ(End)**に並びます。
- 処理(データを取り出す)は、常に**一番前(Front)**にいる人から行われます。
このFIFOの特性は、タスクの待機列(例: 印刷ジョブ、リクエスト処理)など、「受け付けた順番通りに」処理を実行したいロジックを実装するのに最適です。
Queue<T>の基本的な操作
Queue<T>の操作は、List<T>のAddとは異なる、専用のメソッド名(Enqueue, Dequeue)を使用します。
Enqueue(T item): キューの一番後ろに新しい要素を追加(エンキュー)します。Dequeue(): キューの一番前から要素を取り出し、かつ削除(デキュー)します。Peek(): キューの一番前の要素を削除せずに参照(ピーク=覗き見)するだけです。Count: キューに現在含まれている要素の数を取得します。
コード例1:基本的な Enqueue, Dequeue, Peek
プリンターの待機ジョブをQueue<string>で管理する例です。
using System;
using System.Collections.Generic; // Queue<T> を使うために必要
public class QueueBasicExample
{
public static void Main()
{
// 1. string 型の Queue を初期化
var printQueue = new Queue<string>();
// 2. Enqueue: データを待ち行列(キュー)の後ろに追加
printQueue.Enqueue("DocumentA.pdf");
printQueue.Enqueue("ImageB.png");
printQueue.Enqueue("SpreadsheetC.xlsx");
Console.WriteLine($"--- 印刷ジョブが追加されました ---");
Console.WriteLine($"現在の待機ジョブ数: {printQueue.Count}"); // 3
// 3. Peek: 一番前(最初に追加した)の要素を「削除せず」に参照
string nextJob = printQueue.Peek();
Console.WriteLine($"\n次に印刷するジョブ: {nextJob}");
Console.WriteLine($"Peek後のジョブ数: {printQueue.Count}"); // 3 (減っていない)
// 4. Dequeue: 一番前(最初に追加した)の要素を「削除して」取り出す
string currentJob = printQueue.Dequeue();
Console.WriteLine($"\n印刷を実行: {currentJob}");
Console.WriteLine($"Dequeue後のジョブ数: {printQueue.Count}"); // 2 (1つ減った)
// 5. 再度 Peek (先頭が変わっている)
Console.WriteLine($"\n次に印刷するジョブ: {printQueue.Peek()}"); // "ImageB.png"
}
}
出力結果:
--- 印刷ジョブが追加されました ---
現在の待機ジョブ数: 3
次に印刷するジョブ: DocumentA.pdf
Peek後のジョブ数: 3
印刷を実行: DocumentA.pdf
Dequeue後のジョブ数: 2
次に印刷するジョブ: ImageB.png
whileループによるキューの処理
キューに溜まった全要素を、while (queue.Count > 0)ループとDequeueメソッドを使って、FIFO(先入れ先出し)の順序ですべて処理するのは一般的なパターンです。
using System;
using System.Collections.Generic;
public class QueueWhileLoopExample
{
public static void Main()
{
var taskQueue = new Queue<int>();
taskQueue.Enqueue(101); // 1番目に追加
taskQueue.Enqueue(102); // 2番目に追加
taskQueue.Enqueue(103); // 3番目に追加
Console.WriteLine("--- キューの全タスクを処理 (FIFO) ---");
// キューの要素数が 0 より大きい間、ループ
while (taskQueue.Count > 0)
{
// 一番前 (最初に追加した 101) から順番に取り出される
int item = taskQueue.Dequeue();
Console.WriteLine($"Task ID: {item} を処理しました。");
}
Console.WriteLine($"処理後の残存タスク数: {taskQueue.Count}"); // 0
}
}
出力結果:
--- キューの全タスクを処理 (FIFO) ---
Task ID: 101 を処理しました。
Task ID: 102 を処理しました。
Task ID: 103 を処理しました。
処理後の残存タスク数: 0
注意点と安全な操作:TryDequeue / TryPeek
Queue<T>が**空(Countが0)**の状態で、Dequeue()またはPeek()メソッドを呼び出すと、InvalidOperationExceptionという実行時エラーが発生してプログラムが停止します。
これを避けるため、while (queue.Count > 0)のようにCountをチェックする方法の他に、bool値を返すTryDequeueおよびTryPeekメソッド(DictionaryのTryGetValueに似ています)を使用するのが安全な方法です。
using System;
using System.Collections.Generic;
public class QueueTryDequeueExample
{
public static void Main()
{
var taskQueue = new Queue<int>();
taskQueue.Enqueue(900);
// 1. 成功する TryDequeue
if (taskQueue.TryDequeue(out int dequeuedTask))
{
Console.WriteLine($"Dequeue 成功: {dequeuedTask}"); // 900
}
// 2. キューは空になった
// 3. 失敗する TryDequeue (例外は発生しない)
if (taskQueue.TryDequeue(out int failedTask))
{
Console.WriteLine($"Dequeue 成功: {failedTask}");
}
else
{
Console.WriteLine("Dequeue 失敗: キューは空です。");
}
}
}
出力結果:
Dequeue 成功: 900
Dequeue 失敗: キューは空です。
まとめ
Queue<T>は、C#で**FIFO(先入れ先出し)**のロジックを実装するための専用コレクションです。
- 追加:
Enqueue(item)(末尾に追加) - 削除と取得:
Dequeue()(先頭から取得) - 参照のみ:
Peek()(先頭を参照)
DequeueやPeekはキューが空だと例外をスローするため、Count > 0でチェックするか、TryDequeue / TryPeekメソッドを使用することで、安全に操作できます。
