PG日誌

読者です 読者をやめる 読者になる 読者になる

PG日誌

主にc#の事を書いています

List<T>の使い方


f:id:Takachan:20170115150341j:plain

C#で動的に要素を追加したり、削除したりしたいときに使う動的なリストの使い方のメモになります。

基本的な使い方

使う前に以下を宣言する。

using System.Collections.Generic;

単純な宣言

// int型を格納するリストを宣言、intの部分は任意の型が指定できる
var list = new List<int>();

宣言時に内容を指定して初期化

var list = new List<int>()
{
    1, 2, 3, 4 // listには最初から1,2,3,4の4つの要素が含まれている
};

要素の追加/挿入

list.add(5); // 末尾に5を追加
list.Insert(0, 100); // 先頭(0番目)に100を追加

要素の削除

list.Remove(5); // 最初に見つかった5を削除
list.RemoveAt(2); // 2番目の要素を削除
list.RemoveAll(p => p == 2); // 条件(この場合2)に一致するものを全部削除
list.Clear(); // 全部削除

Clearはキャパシティが増えたままなので、長期間使用するなどの場合、必要があればTrimExcess()メソッドをたまに呼び出したほうがメモリに優しいです。

アクセス方法

int item = list[2]; // 2番目の要素にアクセス

 foreach(int num in list) // 先頭から最後まで順番に表示
{
    Console.WriteLine(num);
}

foreach(int num in list.Reverse()) // 最後から先頭へ逆順に表示
{
    Console.WriteLine(num); 
}

ソート

var list = new List<int>() { 5, 2, 6, 3 };
list.Sort(); // 辞書順にソート
 // → 2, 3, 5, 6

list.Sort((a, b) => b - a); // カスタムソートはラムダを書く(逆順にする場合)
// → 6, 5, 3, 2

存在確認

if(list.Exists(p => p == 3)) // 3に一致するものがあるかどうか確認する
{
    // 存在する
}

使用上の注意

Listクラスは、IEnumerableとか、ICollection, IListを継承しているのでなるべくインターフェースで受け渡した方が拡張性が上がります。SortedListとかLinkedList(T)に変更した場合でもシグネチャ変更が発生しません。

public ICollection<int> GetList() { // インターフェースで受け渡す

使い方によってどんなインターフェースを使えばいいかはざっくりとこんな感じです。

interface 状況
IEnumerable(T) 読み取り専用。foreachとかで使ってほしい。無限の長さの場合もこれ
ICollection(T) 追加、削除あり。インデクサでアクセスしない
IList(T) 追加、削除あり。インデクサでアクセスする。配列みたいに使う

(T)が付いていないIList, ICollection, IEnumerableは基本使いません。

プロパティの場合、setterはprivateにしておいた方が安全です。ReadOnlyCollection(T)などに外から変更されて、クラス内で操作して例外が出るなどは想定外かと思います。

public ICollection<int> List { get; private set; } // setはprivateにしておく

やらないほうがいい事

var list = new List<List<int>>(); // <T>の中にジェネリックを宣言しない。

何個でも入れ子にできるのですが外部公開すると理解できない、アクセスを制御できないとコントロール不能なケースが多いのでこういう実装はしない方がいいです。アクセス用のクラスを作りましょう。

あと、Listを直接継承するのではなくIList(T)やICollection(T)を継承したほうが拡張性に優れます。ただ、ライブラリを作成するのでない限り、インターフェースが多機能すぎるので全部自前で実装するより、List(T)を所有するクラスかがアクセス方法を提供するほうが簡単なケースが多いと思います。