C#のコレクションの話です.
どうも特に理由なくList<T>
を漠然と使うようなコードをまま見るので,コレクション型の使い分けについて書いていきます.
TL;DR
コレクション型には様々な種類があり,それそれに違った利点があるので,何でもList<T>
を使うのではなく場面に合ったものを使おうね!
List<T>
が適している場合
List<T>
の一番の強みは,配列のようにインデックスでアクセスできることです.ご存知の通り,list[2]
のような書き方でn番目の要素を取り出すことができます.この順番は勝手に変わったりしないので,順番に意味がある場合にも使えます.
var numList = new List<string>(){ "ぜろ", "いち", "に" };
var item = numList[2]; // "に"
また,要素の中からランダムに1つ選ぶような処理でもよく使われます.0-要素数の範囲で乱数を生成し,その乱数をインデックスとしてアイテムを取り出すことで,リストの中からランダムに1つの要素が得られます.
var random = new Random();
var item1 = numList[random.Next(numList.Count)]; // "に"
var item2 = numList[random.Next(numList.Count)]; // "ぜろ"
var item3 = numList[random.Next(numList.Count)]; // "に"
var item4 = numList[random.Next(numList.Count)]; // "いち"
逆に言えば,これらの用途ではない場合は他の型の使用を検討すべきです.
Stack<T>
, Queue<T>
要素の追加順が大事なときに使うのがこの2つです.
Stack<T>
は,その名の通り上に積み重ねるように要素を追加します.要素を取り出すときは一番上から取り出すので,最後に追加された要素が取り出されます.
Queue<T>
は,要素を順番待ちの行列の一番後ろに追加します.要素を取り出すときは一番前のものを取り出すので,最初に追加された要素から順に取り出されます.
HashSet<T>
順番に意味がなく,要素が重複しない,もしくは重複しているかどうか調べたいときに使います.
HashSet<T>
は,順番に意味を持ちません.そのため,[]
を使ってインデックスでアクセスすることはできません.
また,HashSet<T>
には同じ要素を複数追加することはできません.
Add
メソッドで要素の追加を行いますが,すでに同じ要素が含まれており,追加できなかった場合はfalseが返ります.
この特性を利用して,例えば同じオブジェクトに対しては1回だけ処理を行うような処理を書くことができます.
// ChackUser済のUser
private readonly HashSet<User> checkedUsers = new();
public void CheckUser(User user)
{
// CheckUserしたことがある場合,要素が重複するのでfalseが返る
if (!checkedUsers.Add(user))
{
return;
}
// 以下メソッドの処理
}
まとめ
それぞれに書いたような目的は,極論List<T>
でもLINQやループを使えば達成できますが,当然目的に合ったものを使った方が圧倒的に利点が多いです.
HashSetで例に挙げたような処理をListで行う場合,Containsで重複を確認して,重複していなかったらAddして,と非常にまどろっこしいことになります.
他の利点や計算量などの違いもありますが,状況によってコレクションを使い分けることでパフォーマンスの向上が期待できますし,コードも読みやすくなります.
コレクションに漠然とList<T>
を使う前に,本当にそれが適切なのか,ぜひとも一度見直してみてほしいと思います.
参考