この記事ではStack<T>クラスとQueue<T>クラスについて解説します。以降、Stack<T>クラスのことをStack、Queue<T>クラスのことをQueueと略します。
- StackとQueueとは?
- Stack<T>クラス
- Stackの宣言
- CountプロパティでStackオブジェクトの要素数を取得する
- PushメソッドでStackオブジェクトの先頭に要素を追加する
- PeekメソッドでStackオブジェクトの先頭の要素を取得する
- PopメソッドでStackオブジェクトの先頭の要素を取り出して削除する
- ClearメソッドでStackオブジェクトの全ての要素を削除する
- ContainsメソッドでStackオブジェクトに指定した値を持つ要素が含まれているかを調べる
- TryPeekメソッドでStackオブジェクトの先頭要素の値の取得を試みる
- TryPopメソッドでstackオブジェクトの先頭要素の値の取得と削除を試みる
- ToArrayメソッドでStackオブジェクトを配列に変換する
- CopyToメソッドでStackオブジェクトを配列にコピーする
- Queue<T>クラス
- Queueの宣言
- CountプロパティでQueueオブジェクトの要素数を取得する
- EnqueueメソッドでQueueオブジェトの末尾に要素を追加する
- PeekメソッドでQueueオブジェクトの先頭の要素を取得する
- DequeueメソッドでQueueオブジェクトの先頭の要素を取り出して削除する
- ClearメソッドでQueueオブジェクトの全ての要素を削除する
- ContainsメソッドでQueueオブジェクトに指定した値を持つ要素が含まれているかを調べる
- TryPeekメソッドでQueueオブジェクトの先頭要素の値の取得を試みる
- TryDequeueメソッドでQueueオブジェクトの先頭要素の値の取得と削除を試みる
- ToArrayメソッドでQueueオブジェクトを配列に変換する
- CopyToメソッドでQueueオブジェクトを配列にコピーする
- 終わりに
StackとQueueとは?
StackとQueueは、ジェネリックコレクション(Generic Collection)の一種です。配列のように要素番号はなく、要素を順番に入れたり出したりする点で似ています。ジェネリックコレクションを使うために、下記のusingディレクティブが必要です。
using System.Collection.Generic;Stackは後入れ先出し(LIFO:Last In First Out)と言って、最後に入れたものを先に出します。Queueはその逆で先入れ先出し(FIFO:First In First Out)と言って、先に入れたものを先に出します。
Stackの操作は、エレベータに例えられます。エレベータに人が入っていく順番とエレベータから人が出ていく順番は逆になりますよね。このように最後に入った人が最初に出ていくのがStackです。
Queueの操作は、お店のレジに並ぶ人に例えられます。レジに先に並んだ人から会計をして出ていきます。このように先に並んだ人が先に出ていくのがQueueです。
コレクションでは要素を追加したり取り出したりしますが、StackもQueueも要素を取り出したり削除したりできるのは先頭要素のみです。一方、要素が追加されるとき、Stackは先頭に追加され、Queueは末尾に追加されます。StackとQueueの違いをざっくり言うと、要素の追加する位置が違うということになります。
Stack<T>クラス
Stack<T>クラスは、後入れ先出しの性質を持つジェネリックコレクションです。Tは要素の型を表します。
Stackは、オブジェクトの先頭に要素を追加して、先頭の要素を取り出す用途に使用します。
Stackの宣言
Stackの宣言を見てみましょう。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s1 = new Stack<int>();
var data = new string[] { "一郎", "二郎", "三郎" };
var s2 = new Stack<string>(data);
//var s2 = new Stack<string>(){"一郎", "二郎", "三郎" }; //この構文は不可
foreach(var name in s2)
{
Console.WriteLine(name);
}
}
}出力結果
s2の要素数は3です
三郎
二郎
一郎
上記コードでは8行目でint型の要素を持つStackオブジェクトs1を宣言しています。中身は空です。11行目では、Stackオブジェクトs2を宣言し、string型の配列dataで初期化しています。s2は”一郎”→”次郎”→”三郎”の順でs2の先頭に追加されます。したがって、先頭のデータは”三郎”です。
初期化の注意点として、12行目でコメントアウトしているように、配列などでは{ }を使った初期化子が利用できますが、Stackでは使えません。
また、上記コードのとおりforeach文ですべての要素を先頭から順に読み取ることができます。
CountプロパティでStackオブジェクトの要素数を取得する
CountプロパティでStackオブジェクトの要素数を取得できます。下記コード11行目のとおりです。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var data = new string[] { "一郎", "二郎", "三郎" };
var s = new Stack<string>(data);
Console.WriteLine("sの要素数は{0}です", s.Count);
}
}出力結果
sの要素数は3です
PushメソッドでStackオブジェクトの先頭に要素を追加する
Stackでは要素を追加するときは、Pushメソッドを使ってオブジェクトの先頭に追加します。Pushメソッドの引数は、追加する要素の値です。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
s.Push(1); //要素:1
s.Push(2); //要素:2, 1
s.Push(3); //要素:3, 2, 1
foreach(var i in s)
{
Console.WriteLine(i);
}
}
}出力結果
3
2
1
上記コードでは9~11行目で、int型の要素を持つStackオブジェクトsに、1,2,3の順で要素が追加されているので、先頭から3,2,1の順に並んでいます。したがってforeach文では3,2,1の順に表示されます。
PeekメソッドでStackオブジェクトの先頭の要素を取得する
Stackでは、foreach文ですべての要素を取得することはできても、配列のように要素番号で要素の値を取得したり書き換えたりすることはできません。Stackでできることは先頭の要素を取得することです。Stackでは、Peekメソッドを用いて先頭の要素を取得することができます。Peekメソッドに引数はなく、戻り値は先頭の要素の値です。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine("先頭の要素は{0}です", s.Peek());
foreach (var i in s)
{
Console.WriteLine(i);
}
}
}出力結果
先頭の要素は3です
3
2
1
上記コードでは、13行目でPeekメソッドを使用しています。Peekメソッドでは、Stackオブジェクトには変更は加えられません。
PopメソッドでStackオブジェクトの先頭の要素を取り出して削除する
PopメソッドはStackオブジェクトの先頭の要素の値を取得し、さらにその要素を削除します。Popメソッドに引数はなく、戻り値は取得した先頭の要素の値です。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine("先頭の要素は{0}です", s.Pop());
foreach (var i in s)
{
Console.WriteLine(i);
}
}
}出力結果
先頭の要素は3です
2
1
上記コードでは13行目でPopメソッドを使用しています。このPopメソッドで、Stackオブジェクトの先頭の要素の値である3を取得したうえで、この要素を削除しています。したがってforeach文では削除された3は表示されていません。
ClearメソッドでStackオブジェクトの全ての要素を削除する
ClearメソッドはStackオブジェクトの全ての要素を削除します。Clearメソッドに引数はなく、戻り値もありません。下記コードでは14行目で、Clearメソッドにより全要素を削除しています。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
s.Clear(); //要素を削除
Console.WriteLine("sの要素数は{0}です", s.Count);
foreach (var i in s)
{
Console.WriteLine(i);
}
}
}出力結果
sの要素数は0です
ContainsメソッドでStackオブジェクトに指定した値を持つ要素が含まれているかを調べる
Containsメソッドは、Stackオブジェクトに指定した値が含まれているかを調べます。Containsメソッドには引数として含まれているかを調べる値を指定します。戻り値はbool型で、指定した値が含まれていればtrueを、それ以外の場合にはfalseを返します。
下記のコードでは、13~14行目でContainsメソッドを使用しています。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine("sに2が含まれているか調べます.結果は{0}です.", s.Contains(2));
Console.WriteLine("sに4が含まれているか調べます.結果は{0}です.", s.Contains(4));
}
}出力結果
sに2が含まれているか調べます.結果はTrueです.
sに4が含まれているか調べます.結果はFalseです.
TryPeekメソッドでStackオブジェクトの先頭要素の値の取得を試みる
TryPeekメソッドはStackオブジェクトの先頭要素の値の取得を試みます。引数は一つで、outキーワードを使って取得した先頭要素の値を入れる変数を指定します。戻り値はbool型で取得できた場合にはtrue、それ以外の場合にはfalseを返します。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
Console.WriteLine("TryPeekを実行します.結果は{0},取得した値は{1}です",s.TryPeek(out int val1),val1);
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine("TryPeekを実行します.結果は{0},取得した値は{1}です", s.TryPeek(out int val2), val2);
}
}出力結果
TryPeekを実行します.結果はFalse,取得した値は0です
TryPeekを実行します.結果はTrue,取得した値は3です
上記コードでは10行目と16行目でTryPeekメソッドを使用しています。
10行目では要素はないので、TryPeekメソッドの戻り値はfalseです。引数のval1で受け取る値はint型の既定値の0です。この場合、先頭要素が0でないことに注意が必要です。
13行目では変数val2で先頭要素の3を受け取ります。正常に値を取得できたのでTryPeekメソッドの戻り値はtrueです。
TryPopメソッドでstackオブジェクトの先頭要素の値の取得と削除を試みる
TryPopメソッドはStackオブジェクトの先頭要素の値の取得と削除を試みます。引数は一つで、outキーワードを使って取得した先頭要素の値を入れる変数を指定します。戻り値はbool型で取得・削除できた場合にはtrue、それ以外の場合にはfalseを返します。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
Console.WriteLine("TryPopを実行します.結果は{0},取得した値は{1}です", s.TryPop(out int val1), val1);
s.Push(1);
s.Push(2);
s.Push(3);
Console.WriteLine("TryPopを実行します.結果は{0},取得した値は{1}です", s.TryPop(out int val2), val2);
foreach (var i in s)
{
Console.WriteLine(i);
}
}
}出力結果
TryPopを実行します.結果はFalse,取得した値は0です
TryPopを実行します.結果はTrue,取得した値は3です
2
1
上記コードでは10行目と16行目でTryPopメソッドが使用されています。
10行目ではStackオブジェクトは空なので、先頭の要素は存在せず、値が取得できないため、TryPopメソッドの戻り値はfalseです。val1にはint型の既定値である0が入ります。
16行目では、先頭要素の値である3が取得されval2にコピーされます。要素が取得・削除されたため、TryPopメソッドの戻り値はtrueとなります。この後のforeach文で先頭要素の3が削除されていることがわかります。
ToArrayメソッドでStackオブジェクトを配列に変換する
ToArrayメソッドでStackオブジェクトを配列に変換します。引数はありません。戻り値はStackオブジェクトの要素と同じ型の配列です。下記コードでは13行目でint型の配列に変換されています。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
s.Push(1);
s.Push(2);
s.Push(3);
int[] s_array = s.ToArray();
for (int i = 0; i < s_array.Length; i++)
{
Console.WriteLine(s_array[i]);
}
}
}出力結果
3
2
1
CopyToメソッドでStackオブジェクトを配列にコピーする
CopyToメソッドでは、Stackオブジェクトを配列にコピーします。CoptToメソッドの引数はふたつあり、ひとつめにはコピー先の配列を指定し、ふたつめの引数には配列の何要素目からコピーを開始するかを指定するint型の値を指定します。戻り値はありません。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var s = new Stack<int>();
for (int i = 0; i < 5; i++)
{
s.Push(i);
}
var array = new int[10];
for (int i = 0; i < array.Length; i++)
{
array[i] = 10;
}
s.CopyTo(array, 3);
Console.WriteLine("[{0}]", string.Join(" ", array));
}
}出力結果
[10 10 10 4 3 2 1 0 10 10]
上記コードでは20行目でCopyToメソッドを使用してます。ここのCopyToメソッドでは配列arrayの要素番号3以降にStackオブジェクトsをコピーしています。この時コピー先の配列はStackオブジェクトがコピーできるだけの要素数が必要です。コピーされた部分の配列の要素は書き換えられます。
Queue<T>クラス
Queue<T>クラスは、先入れ先出しの性質を持つジェネリックコレクションです。Tは要素の型を表します。
Stackは先頭に要素を追加して先頭の要素を取り出しましたが、Queueはオブジェクトの末尾に要素を追加して、先頭の要素を取り出す用途に使用します。
Queueの宣言
Queueの宣言を見てみましょう。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q1 = new Queue<int>();
var data = new string[] { "一郎", "二郎", "三郎" };
var q2 = new Queue<string>(data);
//var q2 = new Queue<string>(){"一郎", "次郎", "三郎" }; //この構文は不可
Console.WriteLine("q2の要素数は{0}です", q2.Count);
foreach (var name in q2)
{
Console.WriteLine(name);
}
}
}出力結果
q2の要素数は3です
一郎
二郎
三郎
上記コードでは8行目でint型の要素を持つQueueオブジェクトq1を宣言しています。中身は空です。11行目では、Queueオブジェクトq2を宣言し、string型の配列dataで初期化しています。q2は”一郎”→”次郎”→”三郎”の順でq2の末尾に追加されます。したがって、先頭のデータは”一郎”です。この点はStackと逆になるので注意です。初期化の注意点として、12行目でコメントアウトしているように、配列などでは{ }を使った初期化子が利用できますが、Queueでは使えません。
Queueの要素数は、14行目のとおりCountプロパティで取得できます。
また、上記コードのとおりforeach文ですべての要素を先頭から順に読み取ることができます。
CountプロパティでQueueオブジェクトの要素数を取得する
CountプロパティでQueueオブジェクトの要素数を取得できます。下記コード11行目のとおりです。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var data = new string[] { "一郎", "二郎", "三郎" };
var q = new Queue<string>(data);
Console.WriteLine("q2の要素数は{0}です", q.Count);
}
}出力結果
qの要素数は3です
EnqueueメソッドでQueueオブジェトの末尾に要素を追加する
Queueでは要素を追加するときは、Enqueueメソッドを使ってオブジェクトの末尾に追加します。Enqueueメソッドの引数は、追加する要素の値です。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
q.Enqueue(1); //要素:1
q.Enqueue(2); //要素:1, 2
q.Enqueue(3); //要素:1, 2, 3
foreach (var i in q)
{
Console.WriteLine(i);
}
}
}出力結果
1
2
3
上記コードでは9~11行目で、int型の要素を持つQueueオブジェクトqに、1,2,3の順で要素が追加されているので、先頭から1,2,3の順に並んでいます。したがってforeach文では1,2,3の順に表示されます。Stackと逆ですね。
PeekメソッドでQueueオブジェクトの先頭の要素を取得する
Queueでは、foreach文ですべての要素を取得することはできても、配列のように要素番号で要素の値を取得したり書き換えたりすることはできません。Queueでできることは先頭の要素を取得することです。Queueでは、Peekメソッドを用いて先頭の要素を取得することができます。Peekメソッドに引数はなく、戻り値は先頭の要素の値です。PeekメソッドについてはStackと同じです。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
Console.WriteLine("先頭の要素は{0}です", q.Peek());
foreach (var i in q)
{
Console.WriteLine(i);
}
}
}出力結果
先頭の要素は1です
1
2
3
上記コードでは、13行目でPeekメソッドを使用しています。Peekメソッドでは、Queueオブジェクトには変更は加えられません。
DequeueメソッドでQueueオブジェクトの先頭の要素を取り出して削除する
DequeueメソッドはQueueオブジェクトの先頭の要素の値を取得し、さらにその要素を削除します。Dequeueメソッドに引数はなく、戻り値は取得した先頭の要素の値です。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
Console.WriteLine("先頭の要素は{0}です", q.Dequeue());
foreach (var i in q)
{
Console.WriteLine(i);
}
}
}出力結果
先頭の要素は1です
2
3
上記コードでは13行目でDequeueメソッドを使用しています。このDequeueメソッドで、Queueオブジェクトの先頭の要素の値である1を取得したうえで、この要素を削除しています。したがってforeach文では削除された1は表示されていません。
ClearメソッドでQueueオブジェクトの全ての要素を削除する
ClearメソッドはQueueオブジェクトの全ての要素を削除します。Clearメソッドに引数はなく、戻り値もありません。下記コードでは14行目で、Clearメソッドにより全要素を削除しています。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
q.Clear(); //要素を削除
Console.WriteLine("qの要素数は{0}です", q.Count);
foreach (var i in q)
{
Console.WriteLine(i);
}
}
}出力結果
qの要素数は0です
ContainsメソッドでQueueオブジェクトに指定した値を持つ要素が含まれているかを調べる
Containsメソッドは、Queueオブジェクトに指定した値が含まれているかを調べます。Containsメソッドには引数として含まれているかを調べる値を指定します。戻り値はbool型で、指定した値が含まれていればtrueを、それ以外の場合にはfalseを返します。
下記のコードでは、13~14行目でContainsメソッドを使用しています。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
Console.WriteLine("qに2が含まれているか調べます.結果は{0}です.", q.Contains(2));
Console.WriteLine("qに4が含まれているか調べます.結果は{0}です.", q.Contains(4));
}
}出力結果
qに2が含まれているか調べます.結果はTrueです.
qに4が含まれているか調べます.結果はFalseです.
TryPeekメソッドでQueueオブジェクトの先頭要素の値の取得を試みる
TryPeekメソッドはQueueオブジェクトの先頭要素の値の取得を試みます。引数は一つで、outキーワードを使って取得した先頭要素の値を入れる変数を指定します。戻り値はbool型で取得できた場合にはtrue、それ以外の場合にはfalseを返します。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
Console.WriteLine("TryPeekを実行します.結果は{0},取得した値は{1}です", q.TryPeek(out int val1), val1);
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
Console.WriteLine("TryPeekを実行します.結果は{0},取得した値は{1}です", q.TryPeek(out int val2), val2);
}
}出力結果
TryPeekを実行します.結果はFalse,取得した値は0です
TryPeekを実行します.結果はTrue,取得した値は1です
上記コードでは10行目と16行目でTryPeekメソッドを使用しています。
10行目では要素はないので、TryPeekメソッドの戻り値はfalseです。引数のval1で受け取る値はint型の既定値の0です。この場合、先頭要素が0でないことに注意が必要です。
13行目では変数val2で先頭要素の1を受け取ります。正常に値を取得できたのでTryPeekメソッドの戻り値はtrueです。
TryDequeueメソッドでQueueオブジェクトの先頭要素の値の取得と削除を試みる
TryDequeueメソッドはQueueオブジェクトの先頭要素の値の取得と削除を試みます。引数は一つで、outキーワードを使って取得した先頭要素の値を入れる変数を指定します。戻り値はbool型で取得・削除できた場合にはtrue、それ以外の場合にはfalseを返します。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
Console.WriteLine("TryDequeueを実行します.結果は{0},取得した値は{1}です", q.TryDequeue(out int val1), val1);
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
Console.WriteLine("TryDequeueを実行します.結果は{0},取得した値は{1}です", q.TryDequeue(out int val2), val2);
foreach (var i in q)
{
Console.WriteLine(i);
}
}
}出力結果
TryDequeueを実行します.結果はFalse,取得した値は0です
TryDequeueを実行します.結果はTrue,取得した値は1です
2
3
上記コードでは10行目と16行目でTryDequeueメソッドが使用されています。
10行目ではQueueオブジェクトは空なので、先頭の要素は存在せず、値が取得できないため、TryDequeueメソッドの戻り値はfalseです。val1にはint型の既定値である0が入ります。
16行目では、先頭要素の値である1が取得されval2にコピーされます。要素が取得・削除されたため、TryDequeueメソッドの戻り値はtrueとなります。この後のforeach文で先頭要素の1が削除されていることがわかります。
ToArrayメソッドでQueueオブジェクトを配列に変換する
ToArrayメソッドで}Queueオブジェクトを配列に変換します。引数はありません。戻り値はQueueオブジェクトの要素と同じ型の配列です。下記コードでは13行目でint型の配列に変換されています。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(3);
int[] q_array = q.ToArray();
for (int i = 0; i < q_array.Length; i++)
{
Console.WriteLine(q_array[i]);
}
}
}出力結果
1
2
3
CopyToメソッドでQueueオブジェクトを配列にコピーする
CopyToメソッドでは、Stackオブジェクトを配列にコピーします。CoptToメソッドの引数はふたつあり、ひとつめにはコピー先の配列を指定し、ふたつめの引数には配列の何要素目からコピーを開始するかを指定するint型の値を指定します。戻り値はありません。
コード
using System;
using System.Collections.Generic;
internal class Program
{
static void Main(string[] args)
{
var q = new Queue<int>();
for (int i = 0; i < 5; i++)
{
q.Enqueue(1);
}
var array = new int[10];
for (int i = 0; i < array.Length; i++)
{
array[i] = 10;
}
q.CopyTo(array, 3);
Console.WriteLine("[{0}]", string.Join(" ", array));
}
}出力結果
[10 10 10 1 1 1 1 1 10 10]
上記コードでは20行目でCopyToメソッドを使用してます。ここのCopyToメソッドでは配列arrayの要素番号3以降にQueueオブジェクトqをコピーしています。この時コピー先の配列はQueueオブジェクトがコピーできるだけの要素数が必要です。コピーされた部分の配列の要素は書き換えられます。
終わりに
これでStackとQueueの解説は終わりです。StackとQueueは似ていますので、まとめて覚えましょう。

