Stack<T>とは何か? LIFO(後入れ先出し)
C#のSystem.Collections.Generic名前空間には、List<T>やDictionary<TKey, TValue>の他に、Stack<T>という特定の操作に特化したコレクションがあります。
Stack<T>(スタック)は、「LIFO (Last-In, First-Out)=後入れ先出し」というデータ構造を実装したものです。
これは、現実世界の「積み上げられた皿」や「重ねた本」に例えられます。
- 新しい皿(データ)は、常に**一番上(Top)**に積みます。
- 皿(データ)を取り出すときも、常に**一番上(Top)**から取ります。
このLIFOの特性は、アプリケーションの「元に戻す(Undo)」機能や、Webブラウザの「戻る」ボタンの履歴管理など、”最後に行った操作を最初に取り消す”ロジックを実装するのに最適です。
Stack<T>の基本的な操作
Stack<T>の操作は、List<T>のAddやRemoveとは異なる、専用のメソッド名(Push, Pop)を使用します。
Push(T item): スタックの一番上に新しい要素を追加(プッシュ)します。Pop(): スタックの一番上から要素を取り出し、かつ削除(ポップ)します。Peek(): スタックの一番上の要素を削除せずに参照(ピーク=覗き見)するだけです。Count: スタックに現在含まれている要素の数を取得します。
コード例1:基本的な Push, Pop, Peek
シンプルなテキストエディタの「元に戻す(Undo)」履歴をStack<string>で管理する例です。
using System;
using System.Collections.Generic; // Stack<T> を使うために必要
public class StackBasicExample
{
public static void Main()
{
// 1. string 型の Stack を初期化
var undoHistory = new Stack<string>();
// 2. Push: 操作履歴をスタックの一番上に追加
undoHistory.Push("Action: 'Hello' と入力");
undoHistory.Push("Action: 'World' と入力");
undoHistory.Push("Action: '!' と入力");
Console.WriteLine($"--- 履歴が積まれました ---");
Console.WriteLine($"現在の履歴の数: {undoHistory.Count}"); // 3
// 3. Peek: 一番上の要素を「削除せず」に参照
// 最後に積んだ "Action: '!' と入力" が返る
string nextUndoAction = undoHistory.Peek();
Console.WriteLine($"\n次に'元に戻す'操作: {nextUndoAction}");
Console.WriteLine($"Peek後の履歴の数: {undoHistory.Count}"); // 3 (減っていない)
// 4. Pop: 一番上の要素を「削除して」取り出す
string undoneAction = undoHistory.Pop();
Console.WriteLine($"\n'元に戻す'を実行: {undoneAction}");
Console.WriteLine($"Pop後の履歴の数: {undoHistory.Count}"); // 2 (1つ減った)
// 5. 再度 Peek
Console.WriteLine($"\n次に'元に戻す'操作: {undoHistory.Peek()}"); // "Action: 'World' と入力"
}
}
出力結果:
--- 履歴が積まれました ---
現在の履歴の数: 3
次に'元に戻す'操作: Action: '!' と入力
Peek後の履歴の数: 3
'元に戻す'を実行: Action: '!' と入力
Pop後の履歴の数: 2
次に'元に戻す'操作: Action: 'World' と入力
whileループによるスタックの処理
スタックに積まれた全要素を、while (stack.Count > 0)ループとPopメソッドを使って、LIFO(後入れ先出し)の順序で取り出すのは一般的なパターンです。
using System;
using System.Collections.Generic;
public class StackWhileLoopExample
{
public static void Main()
{
var numberStack = new Stack<int>();
numberStack.Push(10);
numberStack.Push(20);
numberStack.Push(30);
Console.WriteLine("--- スタックの全要素を Pop (LIFO) ---");
// スタックの要素数が 0 より大きい間、ループ
while (numberStack.Count > 0)
{
// 一番上 (最後に追加した 30) から順番に取り出される
int item = numberStack.Pop();
Console.WriteLine(item);
}
Console.WriteLine($"処理後の要素数: {numberStack.Count}"); // 0
}
}
出力結果:
--- スタックの全要素を Pop (LIFO) ---
30
20
10
処理後の要素数: 0
注意点と安全な操作:TryPop / TryPeek
Stack<T>が**空(Countが0)**の状態で、Pop()またはPeek()メソッドを呼び出すと、InvalidOperationExceptionという実行時エラーが発生してプログラムが停止します。
これを避けるため、while (stack.Count > 0)のようにCountをチェックする方法の他に、bool値を返すTryPopおよびTryPeekメソッド(DictionaryのTryGetValueに似ています)を使用するのが安全な方法です。
using System;
using System.Collections.Generic;
public class StackTryPopExample
{
public static void Main()
{
var numberStack = new Stack<int>();
numberStack.Push(100);
// 1. 成功する TryPop
if (numberStack.TryPop(out int poppedValue))
{
Console.WriteLine($"Pop 成功: {poppedValue}"); // 100
}
// 2. スタックは空になった
// 3. 失敗する TryPop (例外は発生しない)
if (numberStack.TryPop(out int failedValue))
{
Console.WriteLine($"Pop 成功: {failedValue}");
}
else
{
Console.WriteLine("Pop 失敗: スタックは空です。");
}
}
}
出力結果:
Pop 成功: 100
Pop 失敗: スタックは空です。
まとめ
Stack<T>は、C#で**LIFO(後入れ先出し)**のロジックを実装するための専用コレクションです。
- 追加:
Push(item) - 削除と取得:
Pop() - 参照のみ:
Peek()
PopやPeekはスタックが空だと例外をスローするため、Count > 0でチェックするか、TryPop / TryPeekメソッドを使用することで、安全に操作できます。
