C# Queue.Peekの使い方|先頭要素を削除せず取得する方法とDequeueとの違い
はじめに
C#で待ち行列のようなデータ構造を扱うときによく使われるのがQueueです。Queueは、先に追加した要素から順番に取り出す「FIFO(First In, First Out)」の仕組みを持っています。
その中でもQueue.Peekは、先頭にある要素を確認したいときに使うメソッドです。Dequeueと似ていますが、Peekは要素を削除しない点が大きな違いです。
この記事では、C#のQueue.Peekの基本的な使い方、Dequeueとの違い、空のQueueで発生する例外、安全に取得するためのTryPeek、実務での活用例までわかりやすく解説します。
1. C#のQueue.Peekとは
Queue.Peekとは、Queueの先頭にある要素を削除せずに取得するためのメソッドです。
Queueは「待ち行列」とも呼ばれ、最初に追加されたデータが最初に取り出されます。たとえば、受付番号、タスク処理、メッセージ処理など、順番に処理したいデータを管理する場面でよく使われます。
1-1. Queue.Peekでできること
Peekを使うと、Queueの先頭要素を確認できます。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
queue.Enqueue("C");
string first = queue.Peek();
Console.WriteLine(first); // A
このコードでは、Queueの先頭にある"A"を取得しています。ただし、Peekを実行しても"A"はQueueから削除されません。
1-2. 先頭要素を削除せずに取得できる仕組み
Queueでは、要素は追加された順番に並びます。
C#A → B → C
この状態でPeekを実行すると、先頭のAを参照します。
C#queue.Peek(); // Aを取得
しかし、Queueの中身は変わりません。
C#A → B → C
つまり、Peekは「次に処理される要素を確認するだけ」のメソッドです。実際に取り出して削除したい場合はDequeueを使います。
1-3. Queue<T>.Peekと非ジェネリックQueue.Peekの違い
C#には、主に2種類のQueueがあります。
C#Queue<T>
と
C#Queue
です。
Queue<T>はジェネリックコレクションで、型を指定して使います。
C#Queue<int> numbers = new Queue<int>();
numbers.Enqueue(10);
int value = numbers.Peek();
一方、非ジェネリックのQueueはSystem.Collections名前空間にあり、要素をobjectとして扱います。
C#Queue queue = new Queue();
queue.Enqueue(10);
object value = queue.Peek();
現在のC#では、基本的にQueue<T>を使うのがおすすめです。型が明確になり、キャストの手間や実行時エラーを減らせるためです。
1-4. Peekが使われる代表的な場面
Peekは、次に処理する要素を確認したいが、まだQueueから削除したくない場面で使います。
たとえば、次のようなケースです。
C#Queue<string> tasks = new Queue<string>();
tasks.Enqueue("メール送信");
tasks.Enqueue("PDF作成");
tasks.Enqueue("通知処理");
Console.WriteLine($"次の処理: {tasks.Peek()}");
このように、処理前の確認、条件判定、ログ出力、順番待ちの確認などで便利です。
2. Queue.Peekの基本的な使い方
ここからは、Queue.Peekの基本的な使い方をコード付きで解説します。
2-1. Queue<T>を使うための準備
Queue<T>を使うには、System.Collections.Generic名前空間を使用します。
C#using System;
using System.Collections.Generic;
基本的なQueueの作成方法は次のとおりです。
C#Queue<string> queue = new Queue<string>();
この例では、文字列を格納するQueueを作成しています。
2-2. Queueに要素を追加するEnqueueの基本
Queueに要素を追加するにはEnqueueを使います。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("Apple");
queue.Enqueue("Banana");
queue.Enqueue("Cherry");
この場合、Queueの中身は次の順番になります。
C#Apple → Banana → Cherry
最初に追加されたAppleが先頭要素です。
2-3. Peekで先頭要素を取得するサンプルコード
Peekを使うと、先頭要素を取得できます。
C#using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Queue<string> queue = new Queue<string>();
queue.Enqueue("Apple");
queue.Enqueue("Banana");
queue.Enqueue("Cherry");
string first = queue.Peek();
Console.WriteLine(first);
}
}
実行結果は次のとおりです。
C#Apple
Peekは先頭要素を返すだけなので、Queueの中身はそのまま残ります。
2-4. Peek実行後もQueue内の要素が残ることを確認する
Peekを実行しても要素が削除されないことを、Countで確認してみましょう。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("Apple");
queue.Enqueue("Banana");
queue.Enqueue("Cherry");
Console.WriteLine(queue.Count); // 3
string first = queue.Peek();
Console.WriteLine(first); // Apple
Console.WriteLine(queue.Count); // 3
Peekの前後でCountは変わりません。つまり、PeekはQueueの状態を変更しないメソッドです。
2-5. string型・int型・独自クラスでの使用例
Queue<T>.Peekは、さまざまな型で使えます。
string型の例です。
C#Queue<string> names = new Queue<string>();
names.Enqueue("Tanaka");
names.Enqueue("Suzuki");
Console.WriteLine(names.Peek()); // Tanaka
int型の例です。
C#Queue<int> numbers = new Queue<int>();
numbers.Enqueue(100);
numbers.Enqueue(200);
Console.WriteLine(numbers.Peek()); // 100
独自クラスでも使えます。
C#class TaskItem
{
public string Name { get; set; }
public TaskItem(string name)
{
Name = name;
}
}
Queue<TaskItem> tasks = new Queue<TaskItem>();
tasks.Enqueue(new TaskItem("データ読み込み"));
tasks.Enqueue(new TaskItem("データ保存"));
TaskItem nextTask = tasks.Peek();
Console.WriteLine(nextTask.Name); // データ読み込み
このように、Queue<T>では型を指定できるため、独自クラスのオブジェクトも安全に扱えます。
3. Queue.PeekとDequeueの違い
Queue.Peekとよく比較されるのがDequeueです。どちらもQueueの先頭要素を扱いますが、動作は異なります。
3-1. Peekは先頭要素を参照する
Peekは、先頭要素を取得しますが削除しません。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
string value = queue.Peek();
Console.WriteLine(value); // A
Console.WriteLine(queue.Count); // 2
Peek後もQueueの要素数は変わりません。
3-2. Dequeueは先頭要素を取得して削除する
Dequeueは、先頭要素を取得したうえでQueueから削除します。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
string value = queue.Dequeue();
Console.WriteLine(value); // A
Console.WriteLine(queue.Count); // 1
Dequeue後は、先頭だったAが削除され、QueueにはBだけが残ります。
3-3. PeekとDequeueの動作比較表
| メソッド | 先頭要素を取得 | 要素を削除 | Queueの要素数 | 主な用途 |
|---|---|---|---|---|
| Peek | する | しない | 変わらない | 次の要素を確認する |
| Dequeue | する | する | 減る | 要素を取り出して処理する |
Peekは確認用、Dequeueは取り出し用と考えるとわかりやすいです。
3-4. 同じQueueに対してPeekとDequeueを実行した結果の違い
同じQueueに対して、PeekとDequeueを実行してみます。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
queue.Enqueue("C");
Console.WriteLine(queue.Peek()); // A
Console.WriteLine(queue.Count); // 3
Console.WriteLine(queue.Dequeue()); // A
Console.WriteLine(queue.Count); // 2
Console.WriteLine(queue.Peek()); // B
最初のPeekではAを取得しますが、Queueからは削除されません。
その後、DequeueでAを取得すると、今度はQueueから削除されます。次にPeekすると、先頭要素はBになります。
3-5. PeekとDequeueを使い分ける判断基準
PeekとDequeueは、目的に応じて使い分けます。
次に処理する要素を確認したいだけならPeekを使います。
C#string next = queue.Peek();
先頭要素を実際に処理して、Queueから削除したいならDequeueを使います。
C#string item = queue.Dequeue();
条件を確認してから削除したい場合は、Peekで確認して、必要に応じてDequeueする流れが便利です。
C#if (queue.Peek() == "A")
{
string item = queue.Dequeue();
}
4. Queue.Peekを使うときの注意点
Queue.Peekを使うときは、空のQueueに注意が必要です。
4-1. Queueが空の状態でPeekすると例外が発生する
Queueが空の状態でPeekを呼び出すと、例外が発生します。
C#Queue<string> queue = new Queue<string>();
string value = queue.Peek();
このコードは、Queueに要素がないため正常に実行できません。
4-2. InvalidOperationExceptionが発生する原因
空のQueueに対してPeekを実行すると、InvalidOperationExceptionが発生します。
C#System.InvalidOperationException
これは、先頭要素が存在しないのに取得しようとしているためです。
Peekは便利なメソッドですが、Queueが空でないことを前提に動作します。そのため、事前に要素数を確認することが重要です。
4-3. Countプロパティで空チェックしてからPeekする方法
もっとも基本的な対処法は、Countで要素数を確認してからPeekする方法です。
C#Queue<string> queue = new Queue<string>();
if (queue.Count > 0)
{
string value = queue.Peek();
Console.WriteLine(value);
}
else
{
Console.WriteLine("Queueは空です。");
}
このように書けば、空のQueueに対してPeekを呼び出すことを防げます。
4-4. TryPeekを使って安全に先頭要素を取得する方法
TryPeekを使うと、Queueが空かどうかを安全に判定しながら先頭要素を取得できます。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("Apple");
if (queue.TryPeek(out string value))
{
Console.WriteLine($"先頭要素: {value}");
}
else
{
Console.WriteLine("Queueは空です。");
}
TryPeekは、先頭要素を取得できた場合にtrueを返します。Queueが空の場合はfalseを返し、例外は発生しません。
空の可能性があるQueueを扱う場合は、PeekよりもTryPeekのほうが安全です。
4-5. nullが格納されている場合の注意点
参照型のQueueでは、nullを格納できます。
C#Queue<string?> queue = new Queue<string?>();
queue.Enqueue(null);
string? value = queue.Peek();
Console.WriteLine(value == null); // True
この場合、Peekの結果がnullでも、Queueが空とは限りません。
そのため、nullを格納する可能性がある場合は、Peekの戻り値だけで空かどうかを判断しないようにしましょう。
C#if (queue.Count > 0)
{
string? value = queue.Peek();
}
または、TryPeekを使って取得できたかどうかを判定するのがおすすめです。
C#if (queue.TryPeek(out string? value))
{
Console.WriteLine(value ?? "値はnullです");
}
5. Queue.Peekの実践的な活用例
Queue.Peekは、単に先頭要素を表示するだけでなく、実務的な処理でも役立ちます。
5-1. 次に処理するデータを事前確認する
タスクを順番に処理する前に、次のタスク名をログに出したい場合があります。
C#Queue<string> tasks = new Queue<string>();
tasks.Enqueue("ファイル読み込み");
tasks.Enqueue("データ変換");
tasks.Enqueue("データ保存");
if (tasks.Count > 0)
{
Console.WriteLine($"次に処理するタスク: {tasks.Peek()}");
}
この時点では、タスクはQueueから削除されません。実際に処理するときにDequeueを使います。
5-2. 条件に合う場合だけDequeueする
先頭要素を確認し、条件に合う場合だけ取り出す処理です。
C#Queue<int> numbers = new Queue<int>();
numbers.Enqueue(10);
numbers.Enqueue(20);
numbers.Enqueue(30);
if (numbers.Count > 0 && numbers.Peek() >= 10)
{
int value = numbers.Dequeue();
Console.WriteLine($"処理しました: {value}");
}
このように、Peekで条件を確認してからDequeueすると、意図しない削除を防げます。
5-3. タスク処理や待ち行列での利用例
Queueは、順番待ちの処理に適しています。
C#Queue<string> requestQueue = new Queue<string>();
requestQueue.Enqueue("Request-001");
requestQueue.Enqueue("Request-002");
requestQueue.Enqueue("Request-003");
while (requestQueue.Count > 0)
{
Console.WriteLine($"処理予定: {requestQueue.Peek()}");
string request = requestQueue.Dequeue();
Console.WriteLine($"処理中: {request}");
}
Peekで次の処理対象を確認し、Dequeueで実際に取り出して処理しています。
5-4. ログ・メッセージ・イベント処理での利用例
メッセージ処理でもPeekは便利です。たとえば、先頭メッセージの種類を確認してから処理を分岐できます。
C#Queue<string> messages = new Queue<string>();
messages.Enqueue("INFO: 起動しました");
messages.Enqueue("ERROR: 例外が発生しました");
if (messages.Count > 0)
{
string nextMessage = messages.Peek();
if (nextMessage.StartsWith("ERROR"))
{
Console.WriteLine("エラーメッセージを優先確認します。");
}
string message = messages.Dequeue();
Console.WriteLine(message);
}
Peekを使うことで、先頭メッセージの内容を確認してから処理方針を決められます。
5-5. Queue.Peekとループ処理を組み合わせる例
Peekはループ処理と組み合わせることもできます。
C#Queue<int> scores = new Queue<int>();
scores.Enqueue(80);
scores.Enqueue(90);
scores.Enqueue(40);
scores.Enqueue(100);
while (scores.Count > 0)
{
int nextScore = scores.Peek();
if (nextScore < 50)
{
Console.WriteLine($"低いスコアのため処理を停止: {nextScore}");
break;
}
int score = scores.Dequeue();
Console.WriteLine($"処理済みスコア: {score}");
}
この例では、先頭の値が50未満なら処理を停止します。Peekで確認してからDequeueしているため、条件に合わない要素をQueueに残したままにできます。
6. Queue.Peek・TryPeek・Dequeueの使い分け
Queueの先頭要素を扱うメソッドには、Peek、TryPeek、Dequeueがあります。それぞれの役割を理解して使い分けることが大切です。
6-1. 先頭要素を確認するだけならPeek
Queueが空でないことがわかっている場合、先頭要素を確認するだけならPeekを使います。
C#if (queue.Count > 0)
{
string value = queue.Peek();
}
PeekはQueueの中身を変更しないため、確認用途に向いています。
6-2. 空のQueueでも安全に処理したいならTryPeek
Queueが空かもしれない場合は、TryPeekを使うと安全です。
C#if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
else
{
Console.WriteLine("Queueは空です。");
}
TryPeekは、要素を取得できたかどうかをboolで返します。例外処理を書かずに安全なコードにしやすい点がメリットです。
6-3. 先頭要素を取り出して削除するならDequeue
先頭要素を処理済みにしてQueueから削除したい場合はDequeueを使います。
C#if (queue.Count > 0)
{
string value = queue.Dequeue();
Console.WriteLine(value);
}
Dequeueは要素を削除するため、処理済みデータをQueueに残したくない場合に適しています。
6-4. 例外処理を避けたい場合の実装パターン
例外を避けたい場合は、CountまたはTryPeekを使います。
Countを使うパターンです。
C#if (queue.Count > 0)
{
string value = queue.Peek();
Console.WriteLine(value);
}
TryPeekを使うパターンです。
C#if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
空のQueueが通常の処理として起こり得る場合は、TryPeekを使うほうが自然です。
6-5. 実務でおすすめの使い分け方
実務では、次のように使い分けるとわかりやすくなります。
| 目的 | おすすめのメソッド |
|---|---|
| 先頭要素を確認したい | Peek |
| 空の可能性があるQueueから安全に確認したい | TryPeek |
| 先頭要素を取り出して削除したい | Dequeue |
| 条件に合う場合だけ削除したい | PeekまたはTryPeekで確認後、Dequeue |
| 例外を避けたい | TryPeek |
特に、外部入力や非同期処理などでQueueが空になる可能性がある場合は、TryPeekを使うと安全です。
7. Queue.Peekでよくあるエラーと対処法
ここでは、Queue.Peekを使うときによくあるエラーや誤解を紹介します。
7-1. 空のQueueにPeekして例外が出る
もっとも多いエラーは、空のQueueに対してPeekを呼び出すことです。
C#Queue<string> queue = new Queue<string>();
Console.WriteLine(queue.Peek());
このコードはInvalidOperationExceptionになります。
対処法は、Countで確認することです。
C#if (queue.Count > 0)
{
Console.WriteLine(queue.Peek());
}
または、TryPeekを使います。
C#if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
7-2. Peekしたのに要素が減らない
PeekしたのにQueueの要素数が減らないのは正常な動作です。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
queue.Peek();
Console.WriteLine(queue.Count); // 2
Peekは参照するだけで削除しません。要素を減らしたい場合はDequeueを使います。
C#queue.Dequeue();
Console.WriteLine(queue.Count); // 1
7-3. Dequeueとの違いを誤解して処理結果がずれる
PeekとDequeueを混同すると、同じ要素を何度も処理してしまうことがあります。
C#while (queue.Count > 0)
{
string item = queue.Peek();
Console.WriteLine(item);
}
このコードは、Queueの要素数が減らないため無限ループになります。
処理済みの要素を削除したい場合はDequeueを使います。
C#while (queue.Count > 0)
{
string item = queue.Dequeue();
Console.WriteLine(item);
}
確認だけしたい場合はPeek、処理して削除したい場合はDequeueを使いましょう。
7-4. foreach中にQueueを変更してエラーになる
Queueをforeachで列挙している最中に、EnqueueやDequeueで変更するとエラーになることがあります。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
foreach (string item in queue)
{
Console.WriteLine(item);
queue.Dequeue();
}
このような書き方は避けましょう。
Queueから順番に取り出して処理したい場合は、whileを使うのが適切です。
C#while (queue.Count > 0)
{
string item = queue.Dequeue();
Console.WriteLine(item);
}
Peekで確認しながら処理したい場合も、whileと組み合わせると安全です。
C#while (queue.Count > 0)
{
Console.WriteLine($"次の要素: {queue.Peek()}");
string item = queue.Dequeue();
Console.WriteLine($"処理: {item}");
}
7-5. マルチスレッド環境でQueueを扱うときの注意点
通常のQueue<T>は、複数のスレッドから同時に操作する用途には向いていません。
たとえば、あるスレッドでCount > 0を確認した直後に、別のスレッドがDequeueしてQueueを空にする可能性があります。その状態でPeekを呼ぶと、例外が発生することがあります。
C#if (queue.Count > 0)
{
string value = queue.Peek();
}
単一スレッドでは問題ないことが多いですが、マルチスレッド環境では注意が必要です。
複数スレッドで安全にQueueを扱いたい場合は、ConcurrentQueue<T>の利用を検討します。
C#using System.Collections.Concurrent;
ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
queue.Enqueue("A");
if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
ConcurrentQueue<T>では、TryPeekやTryDequeueを使って安全に処理できます。
8. Queue.Peekに関するよくある質問
最後に、Queue.Peekに関するよくある質問をまとめます。
8-1. Queue.Peekは要素を削除しますか
いいえ、Queue.Peekは要素を削除しません。
Peekは先頭要素を取得するだけです。Queueから要素を削除したい場合はDequeueを使います。
C#queue.Peek(); // 削除しない
queue.Dequeue(); // 削除する
8-2. Queue.PeekとQueue.Firstの違いは何ですか
Queue.Peekは、Queueの先頭要素を取得するためのメソッドです。
一方、FirstはLINQの拡張メソッドです。
C#using System.Linq;
string value = queue.First();
どちらも先頭要素を取得できますが、Queueの先頭を扱う目的であればPeekのほうが意図が明確です。
また、PeekはQueue専用の操作であるため、「次に取り出される要素を確認している」という意味がコードから伝わりやすくなります。
8-3. Queueが空かどうかを確認する方法はありますか
Queueが空かどうかは、Countプロパティで確認できます。
C#if (queue.Count == 0)
{
Console.WriteLine("Queueは空です。");
}
Peekする前には、次のように確認すると安全です。
C#if (queue.Count > 0)
{
Console.WriteLine(queue.Peek());
}
また、TryPeekを使えば、空チェックと取得を同時に行えます。
C#if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
8-4. TryPeekはどのバージョンのC#で使えますか
TryPeekはC#の言語機能というより、.NETのライブラリで提供されるメソッドです。そのため、使用できるかどうかはC#のバージョンではなく、利用している.NETのバージョンや対象フレームワークに依存します。
現在の.NET環境でQueue<T>を使う場合は、TryPeekを利用できるケースが一般的です。
C#if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
古い環境でTryPeekが使えない場合は、Countで確認してからPeekする方法を使います。
C#if (queue.Count > 0)
{
string value = queue.Peek();
}
8-5. Stack.Peekとの違いは何ですか
Queue.PeekとStack.Peekは、どちらも要素を削除せずに取得するメソッドです。
違いは、取得する要素の位置です。
QueueはFIFOなので、最初に追加された要素を取得します。
C#Queue<string> queue = new Queue<string>();
queue.Enqueue("A");
queue.Enqueue("B");
Console.WriteLine(queue.Peek()); // A
StackはLIFOなので、最後に追加された要素を取得します。
C#Stack<string> stack = new Stack<string>();
stack.Push("A");
stack.Push("B");
Console.WriteLine(stack.Peek()); // B
つまり、Queue.Peekは「次に取り出される先頭要素」、Stack.Peekは「最後に追加された一番上の要素」を確認します。
まとめ
C#のQueue.Peekは、Queueの先頭要素を削除せずに取得するメソッドです。
QueueはFIFOのデータ構造であり、最初に追加された要素が最初に取り出されます。Peekを使うと、次に処理される要素を事前に確認できます。
PeekとDequeueの違いは、要素を削除するかどうかです。Peekは削除せずに確認するだけ、Dequeueは取得して削除します。
C#queue.Peek(); // 先頭要素を確認するだけ
queue.Dequeue(); // 先頭要素を取得して削除する
ただし、空のQueueに対してPeekを実行するとInvalidOperationExceptionが発生します。そのため、Countで空チェックをするか、TryPeekを使って安全に取得することが重要です。
C#if (queue.TryPeek(out string value))
{
Console.WriteLine(value);
}
実務では、次のように使い分けるとよいでしょう。
| 目的 | 使用するメソッド |
|---|---|
| 先頭要素を確認したい | Peek |
| 空でも安全に確認したい | TryPeek |
| 先頭要素を取り出して削除したい | Dequeue |
Queue.Peekを正しく使うことで、次に処理するデータを確認しながら、安全でわかりやすいQueue処理を実装できます。

