Listクラスのメソッド

Listクラスの操作

ここではよく使われるListクラスのメソッドを紹介します。
Listクラスについての基本的な事柄はListクラスを参照してください。

配列と同じく、最初の要素番号は「0」なので注意してください。

Add(要素の追加)

Listに要素を追加するにはAddメソッドを使用します。


//空のListを作成
var lst = new List<int>();

//要素を末尾に追加
lst.Add(2);
lst.Add(3);
lst.Add(5);

foreach(var n in lst)
    Console.WriteLine(n);
2
3
5

最初に要素数ゼロのListを宣言しています。
配列の場合は要素数がゼロの配列を作ってもほとんど意味がありませんが、Listの場合は後で要素を追加できます。

Addメソッドは、常にListの末尾に新しい要素を追加します。
途中に要素を追加したい場合は次のInsertメソッドを使用します。

Insert(要素の挿入)

Listの途中に要素を追加したい場合はInsertメソッドを使用します。


var lst = new List<string>() { "Apple", "Grape", "Strawberry" };

//要素を1番目に追加
lst.Insert(1, "Orange");

//要素を3番目に追加
lst.Insert(3, "Peach");

for (int i = 0; i < lst.Count; i++)
    Console.WriteLine("{0}: {1}", i, lst[i]);
0: Apple
1: Orange
2: Grape
3: Peach
4: Strawberry

第一引数に追加する要素番号、第二引数に追加したい要素を指定します。
第一引数が0未満、またはListの要素数以上の値の場合はエラー(例外)が発生します。

要素を途中に挿入することで、以降の要素は要素番号がひとつ後ろにずらされることに注意してください。

挿入は速度がやや劣る

Listの末尾に要素を追加するAddメソッドは高速に行うことができます。
対して、途中に要素を挿入するInsertメソッドは、Addに比べると速度が劣ります。

数百件程度の追加ではほぼ差はありませんが、何万件も追加する処理では差が出てきます。
要素を大量に追加する処理を行う場合はInsertの使用は避けるか、別の手段を用いたほうがパフォーマンス的に有利な場合があります。
(LinkedListなど)

AddRange(Listの結合)

Listの末尾に別のListを結合するにはAddRangeメソッドを使用します。


var lst1 = new List<int>() { 1, 2, 3 };
var lst2 = new List<int>() { 4, 5, 6 };

//lst1の末尾にlst2を結合
lst1.AddRange(lst2);

foreach(var n in lst1)
    Console.WriteLine(n);
1
2
3
4
5
6

InsertRange(Listの挿入)

Listの途中に別のListを挿入するにはInsertRangeメソッドを使用します。


var lst1 = new List<string>() { "Apple", "Strawberry", "Peach" };
var lst2 = new List<string>() { "Orange", "Grape" };

//lst1の1番目の要素にlst2を追加
lst1.InsertRange(1, lst2);

foreach(var s in lst1)
    Console.WriteLine(s);
Apple
Orange
Grape
Strawberry
Peach

第一引数に追加する要素番号、第二引数に追加したいListを指定します。
第一引数が0未満、またはListの要素数以上の値の場合はエラー(例外)が発生します。

Insertメソッドと同じく、Listを途中に挿入することによって以降の要素は要素番号が後ろにずらされます。

Remove(要素の削除)

要素の削除にはRemoveメソッドを使用します。


var lst = new List<string>()
	{ "Apple", "Strawberry", "Peach",
    "Orange", "Grape", "Strawberry" };

//要素を削除
bool bo1 = lst.Remove("Peach"); //true
bool bo2 = lst.Remove("Strawberry"); //true
bool bo3 = lst.Remove("Peach"); //false

foreach (var s in lst)
    Console.WriteLine(s);
Apple
Orange
Grape
Strawberry

Removeメソッドは要素を先頭から検索し、最初に見つかった要素を削除します。
削除に成功した場合は真を、要素が見つからなかった場合は偽を返します。

同じ値が複数存在する場合は最初に見つかった要素だけが削除されるので注意してください。

RemoveAt(要素番号を指定して削除)

RemoveAtメソッドは「値」ではなく「要素番号」を指定して要素を削除します。


var lst = new List<string>()
    { "Apple", "Grape", "Orange", "Strawberry", "Peach" };

//要素番号で削除
lst.RemoveAt(1);
lst.RemoveAt(3);

foreach (var s in lst)
    Console.WriteLine(s);
Apple
Orange
Strawberry

0未満、もしくはListの要素数以上の値を指定するとエラー(例外)になります。

最後以外の要素を削除すると、削除した後ろの要素が一つ前に詰められます。
要素番号が変わることに注意が必要です。

Removeメソッドとは異なり、戻り値はありません。

RemoveRange(範囲を指定して削除)

要素の範囲を指定して一括削除するにはRemoveRangeメソッドを使用します。


var lst = new List<string>()
    { "Apple", "Grape", "Orange", "Strawberry", "Peach" };

//要素番号1から2つ分の要素を削除
lst.RemoveRange(1, 2);

oreach (var s in lst)
    Console.WriteLine(s);
Apple
Strawberry
Peach

RemoveRangeメソッドの第一引数には削除を開始する要素番号を指定します。
第二引数は削除する要素数を指定します。

要素の指定が範囲外の場合はエラー(例外)が発生します。

RemoveAll(条件に一致する要素の削除)

ある条件に一致する要素をすべて削除するにはRemoveAllメソッドを使用します。
これは条件判定用のメソッドと、Predicate(術語)という特殊なデータ型を使用して要素を検索し、一致した要素を削除します。


//引数が10以上なら真を返すメソッド
static bool IsGTE10(int n)
{
    return n >= 10;
}

static void Main(string[] args)
{
    var lst = new List<int>() { 8, 3, 16, 19, 2, 31 };
    Predicate<int> isGTE10 = IsGTE10;

    int removed = lst.RemoveAll(isGTE10);

    foreach (var n in lst)
        Console.WriteLine(n);

    Console.WriteLine("削除された要素数: {0}", removed);

    Console.ReadLine();
}
8
3
2
削除された要素数: 3

まず、条件判定用のメソッドを定義します。
今回は10以上の要素をすべて削除するために、引数が10以上ならば真を返すメソッドにします。

次に、「Predicate<int>」というデータ型の変数を用意します。
「<>」の中には、先ほど自作したメソッドの引数のデータ型を指定します。
変数名を記述し、その変数には先ほど定義したメソッドを代入します。
ここでそのメソッドを呼び出すわけではないので、丸括弧(関数呼び出し演算子)は記述しません。
実際のメソッドの呼び出しはRemoveAllメソッドが行います。

このPredicate型変数をRemoveAllメソッドに指定することで、条件に一致する要素をすべて削除することができます。
戻り値は削除された要素数です。

ラムダ式を用いればPridicateと条件判定用のメソッドを用意する必要はなくなります。


var lst = new List<int>() { 8, 3, 16, 19, 2, 31 };

int removed = lst.RemoveAll((x) => x >= 10);

foreach (var n in lst)
    Console.WriteLine(n);

Console.WriteLine("削除された要素数: {0}", removed);
8
3
2
削除された要素数: 3

Clear(要素をすべて削除)

Listの要素をすべて削除するにはClearメソッドを使用します。


var lst = new List<int>() { 0, 1, 2, 3, 4, 5 };

lst.Clear();

//0
Console.WriteLine(lst.Count);

これは特に説明の必要はないでしょう。
Clearメソッド実行後の要素数はゼロになります。

List変数自体が不要になった場合は、変数にnullを代入します。


static void Func()
{
    var lst = new List<int>() { 0, 1, 2, 3, 4, 5 };

    //何か処理...

    lst.Clear();

    lst = null;
}

C#にはガベージコレクションという機能があり、不要になったデータは自動的にメモリ上から消去してくれます。
(ガベージコレクション=ゴミ拾い)
クラスの変数(インスタンス)は参照型ですから、これにnullを代入するということはメモリ上のどこも指していない状態になります。
それまでその変数保存していたデータはどこからも使用されていないことになるため、ガベージコレクションがメモリを解放してくれます。

上記のようなローカル変数は、現在のスコープ(上記の場合はFuncメソッド)を抜けた時点で寿命となるので、これも勝手にガベージコレクションがメモリ消去してくれます。
なので、上記はあまり意味のない記述ですが、メンバー変数の場合はなかなか寿命が来ないことも多く、明示的にメモリ上からデータを消去したい場合はnullを代入するのが有効な方法です。

もちろんnull代入後の変数は使用不可能となります。
ただし、再度初期化することで同じ変数を使いまわすことはできます。

なお、プログラム上のどこかで参照状態が続いている場合は「不要なデータ」ではありませんからガベージコレクションはメモリを消去してくれません。
以下のような場合はlst1が参照していた「0,1,2,3」というデータは、6行目で「lst1」からは参照されなくなりますが、「lst2」からは参照状態が続くので、メモリの消去は行われません。


static void Func()
{
    var lst1 = new List<int>() { 0, 1, 2, 3 };
    var lst2 = lst1;

    lst1 = null;
    //lst1の中身はlst2が参照しているため消えない

    //何か処理...

}//lst2の寿命によりメモリから消去

思わぬところから参照状態が続いているといつまで経ってもメモリが解放されないので注意しましょう。

ToArray(Listを配列に変換)

Listを配列に変換するにはToArrayメソッドを使用します。


var lst = new List<int>() { 1, 2, 3 };

int[] arr = lst.ToArray();

foreach (var n in arr)
    Console.WriteLine(n);
1
2
3

Listの要素が参照型の場合はアドレスのコピーになることに注意してください。
つまりToArray適用後に元のListから値を書き換えると、変換後の配列にも影響します。
(これは大抵のコピー系メソッドに共通です)


var lst = new List<int[]>()
{
    new int[] { 1, 2 },
    new int[] { 3, 4 },
};

int[][] arr = lst.ToArray();

lst[0][0] = 9;

foreach (var x in arr)
    foreach (var y in x)
        Console.WriteLine(y);
9
2
3
4

GetRange(要素の部分コピー)

Listから指定の範囲をコピーするにはGetRangeメソッドを使用します。


var lst1 = new List<int>() { 6, 11, 2, 9, 4 };

//1番目の要素から3つ分をコピー
var lst2 = lst1.GetRange(1, 3);

foreach (var n in lst2)
    Console.WriteLine(n);
11
2
9

Sort(要素の並べ替え)

Listを昇順で並べ替えるにはSortメソッドを使用します。


var lst = new List<int>() { 0, 3, 12, 7, 1 };

lst.Sort();

foreach (var n in lst)
    Console.Write("{0}, ", n);
0, 1, 3, 7, 12,

Listを降順で並べ替えたい場合はReverseメソッドと組み合わせることで可能です。
(以下で説明する並べ替え用メソッドを使用する方法でも可能です)

メソッドを使用した並べ替え

Sortメソッドは並べ替え用のメソッドを使用することで、順序を任意に変更できます。
例えばstring型Listの並べ替えは通常はABC順(あいうえお順)ですが、以下のコードは「文字数が少ない順」「同じ文字数の場合はABC順」というルールで並べ替えをしています。


//Sortメソッドの比較用メソッド
//負数を返すとxが手前になる
//正数を返すとyが手前になる
//ゼロを返すと両者は同じ
static int CompareString(string x, string y)
{
    //nullチェック
    if (x == null && y == null)
        return 0;
    if (x == null)
        return -1;
    if (y == null)
        return 1;

    if (x.Length < y.Length)
        return -1;
    if (x.Length > y.Length)
        return 1;
    //文字数が同じ場合は通常通り
    return string.Compare(x, y);
}

static void Main(string[] args)
{
    var lst = new List<string>()
        { "Tom", "James", "Michael", "Chris", "Boby" };

    lst.Sort();

    foreach (var s in lst)
        Console.Write("{0}, ", s);

    Console.WriteLine();

    //比較用メソッドを使用して並べ替え
    lst.Sort(CompareString);

    foreach (var s in lst)
        Console.Write("{0}, ", s);

    Console.ReadLine();
}
Boby, Chris, James, Michael, Tom,
Tom, Boby, Chris, James, Michael,

並べ替え用メソッドを以下のようにすると、要素を降順で並べ替えることが可能です。


static int CompareReverse(int x, int y)
{
    //xのほうが大きい場合はxを手前にする
    return x == y ? 0 :
        x > y ? -1 : 1;
}

static int CompareReverse(string x, string y)
{
    //xとyを逆にしてCompareする
    return string.Compare(y, x);
}

static void Main(string[] args)
{
    var lstInt = new List<int>()
        { 5, 7, 11, 2, 9 };
    var lstString = new List<string>()
        { "Tom", "James", "Michael", "Chris", "Boby" };

    lstInt.Sort(CompareReverse);
    lstString.Sort(CompareReverse);

    foreach (var n in lstInt)
        Console.Write("{0}, ", n);

    Console.WriteLine();

    foreach (var s in lstString)
        Console.Write("{0}, ", s);

    Console.ReadLine();
}
11, 9, 7, 5, 2,
Tom, Michael, James, Chris, Boby,

Reverse(要素の前後反転)

要素の並びを反転させるにはReverseメソッドを使用します。


var lst = new List<int>()
    { 5, 7, 11, 2, 9 };

lst.Reverse();

foreach (var n in lst)
    Console.Write("{0}, ", n);
9, 2, 11, 7, 5,

第一引数、第二引数に数値を指定することで指定の範囲のみ反転させることができます。


var lst = new List<int>()
    { 5, 7, 11, 2, 9 };

//1番目の要素から3つ分を反転
lst.Reverse(1, 3);

foreach (var n in lst)
    Console.Write("{0}, ", n);
5, 2, 11, 7, 9,

降順で並べ替え

Reverseメソッドは降順ではなく前後の反転です。
降順で並べ替えるにはで昇順に並べ替えてからReverseメソッドを適用します。


var lst = new List<int>()
    { 5, 7, 11, 2, 9 };

lst.Sort();
lst.Reverse();

foreach (var n in lst)
    Console.Write("{0}, ", n);
11, 9, 7, 5, 2,

Contains(要素の存在判定)

List中の要素に特定の値が存在するかを判定するにはContainsメソッドを使用します。


var lst = new List<int>() { 2, 3, 5, 7, 11 };

Console.WriteLine(lst.Contains(7));
Console.WriteLine(lst.Contains(8));
True
False

指定の値が要素内に一つでも存在すれば真を返します。
要素が存在する場所(要素番号)を知りたい場合はIndexOfLastIndexOfメソッドなどを使用します。

IndexOf(要素の検索)

Listから要素を検索するにはIndexOfメソッドを使用します。


var lst = new List<int>() { 2, 3, 5, 7, 11 };

int index1 = lst.IndexOf(7);
int index2 = lst.IndexOf(8);

Console.WriteLine(index1);
Console.WriteLine(index2);
3
-1

IndexOfメソッドは要素を先頭から検索し、最初に見つかった要素の番号を返します。
要素が見つからなかった場合は「-1」を返します。

「ある値が存在するか否か」だけを知りたい場合はContainsメソッドを使用してください。

第二引数、第三引数を指定することで柔軟な検索が可能です。


var lst = new List<int>() { 0, 3, 12, 7, 1, 0, 6 };

//先頭から「0」を検索
int index1 = lst.IndexOf(0);

//2番目の要素以降から「0」を検索
int index2 = lst.IndexOf(0, 2);

//2番目の要素から3つ分まで「0」を検索
int index3 = lst.IndexOf(0, 2, 3);

Console.WriteLine(index1);
Console.WriteLine(index2);
Console.WriteLine(index3);
0
5
-1

例えばList内に含まれる目的の要素をすべて表示するには以下のようにします。


List<int> lst = new List<int> { 0, 12, 16, 0, 7, 0, 3 };
int index = -1;

//「0」が出現する位置をすべて表示
while (true)
{
    //前回見つかった要素以降を検索対象にするために1を加算する
    index = lst.IndexOf(0, index + 1);
    if (index >= 0)
    {
        Console.WriteLine(index);
        continue;
    }
    break;
}
0
3
5

LastIndexOf(要素の後方検索)

IndexOfメソッドは要素を先頭から検索しますが、後方から検索したい場合はLastIndexOfメソッドを使用します。


var lst = new List<int>() { 0, 3, 12, 7, 1, 0, 6 };

//前方検索
int index = lst.IndexOf(0);

//後方検索
int lastIndex = lst.LastIndexOf(0);

Console.WriteLine(index);
Console.WriteLine(lastIndex);
0
5

検索が後方から行われる以外はIndexOfメソッドと同じです。
第二引数、第三引数を指定して検索範囲を絞れるのも同じです。

戻り値は後ろから数えた番号ではなく要素番号なので注意してください。

BinarySearch(要素の二分検索)

要素の検索にはIndexOfメソッドを用いますが、BinarySearchメソッドを使用する方法もあります。
BinarySearchは二分検索という方法で検索するメソッドで、先頭から順に検索するIndexOfメソッドよりも高速な検索が可能です。
ただしBinarySearchメソッドは検索対象があらかじめ昇順で並べ替えられている必要があります。
つまり、BinarySearchメソッドを使用する前にSortメソッドを適用しておく必要があります。


var lst = new List<int>()
    { 5, 7, 11, 2, 9 };

var item = 7;

lst.Sort();

int index = lst.BinarySearch(item);

foreach (var n in lst)
    Console.Write("{0}, ", n);
Console.WriteLine();

Console.WriteLine("「{0}」は{1}番目", item, index);
2, 5, 7, 9, 11,
「7」は2番目

戻り値はIndexOfメソッドと同じく要素番号です。

Find(条件に合致する要素の取得)

ある条件を指定して、合致する要素を取得するにはFindメソッドを使用します。
FindメソッドはRemoveAllメソッドと同じくPredicate(術語)を使用します。


//引数が10以上なら真を返すメソッド
static bool IsGTE10(int n)
{
    return n >= 10;
}

static void Main(string[] args)
{
    var lst = new List<int>() { 8, 3, 16, 19, 2, 31 };

    Predicate<int> isGTE10 = IsGTE10;

    int find = lst.Find(isGTE10);

    Console.WriteLine(find);

    Console.ReadLine();
}
16

サンプルコードでは10以上の要素をList内から検索し、最初に見つかった要素を返します。
(要素番号ではなく要素自体を返す)
見つからなかった場合は規定値を返します。
(int型なら「0」)

Findメソッドの仲間

Predicateを使用して条件判定を行うメソッドは他にも存在します。
使い方はFindメソッドと同じなので、具体的な説明は以下のコードを参照してください。


static bool IsGTE10(int n)
{
    return n >= 10;
}

static void Main(string[] args)
{
    var lst = new List<int>() { 8, 3, 16, 19, 2, 31 };
    Predicate<int> isGTE10 = IsGTE10;

    //最初に見つかった要素を返す
    int find =
        lst.Find(isGTE10);

    //最後に見つかった要素を返す
    int findLast =
        lst.FindLast(isGTE10);

    //最初に見つかった要素の要素番号を返す
    int findIndex =
        lst.FindIndex(isGTE10);

    //最後に見つかった要素の要素番号を返す
    int findLastIndex =
        lst.FindLastIndex(isGTE10);

    //条件に合致するすべての要素をListで返す
    List<int> findAll =
        lst.FindAll(isGTE10);

    //条件に合致する要素が存在するかを真偽値で返す
    bool exists =
        lst.Exists(isGTE10);

    //すべての要素が条件に合致するかを真偽値で返す
    bool trueForAll =
        lst.TrueForAll(isGTE10);

    Console.Write("検索対象のList: ");
    foreach (var n in lst)
        Console.Write("{0}, ", n);
    Console.WriteLine("\n検索条件: n >= 10\n");

    Console.WriteLine("Find: {0}", find);
    Console.WriteLine("FindLast: {0}", findLast);
    Console.WriteLine("FindIndex: {0}", findIndex);
    Console.WriteLine("FindLastIndex: {0}", findLastIndex);

    Console.Write("FindAll: ");
    foreach (var n in findAll)
        Console.Write("{0}, ", n);
    Console.WriteLine();

    Console.WriteLine("Exists: {0}", exists);
    Console.WriteLine("TrueForAll: {0}", trueForAll);

    Console.ReadLine();
}
検索対象のList: 8, 3, 16, 19, 2, 31,
検索条件: x >= 10

Find: 16
FindLast: 31
FindIndex: 2
FindLastIndex: 5
FindAll: 16, 19, 31,
Exists: True
TrueForAll: False

FindIndexメソッドとFindLastIndexメソッドは、要素が見つからなかった場合は「-1」を返します。
またこれらのメソッドにはオーバーロードが存在します。


static bool IsGTE10(int n)
{
    return n >= 10;
}

static void Main(string[] args)
{
    var lst = new List<int>() { 0, 1, 12, 3, 4, 15 };
    Predicate<int> isGTE10 = IsGTE10;

    //3番目の要素から検索開始
    int findIndex1 = lst.FindIndex(3, isGTE10);

    //3番目の要素から2つ分を検索
    int findIndex2 = lst.FindIndex(3, 2, isGTE10);

    //4番目の要素から検索開始
    int findLastIndex1 = lst.FindLastIndex(4, isGTE10);

    //4番目の要素から2つ分を検索
    int findLastIndex2 = lst.FindLastIndex(4, 2, isGTE10);

    Console.WriteLine(findIndex1);
    Console.WriteLine(findIndex2);

    Console.WriteLine(findLastIndex1);
    Console.WriteLine(findLastIndex2);

    Console.ReadLine();
}
5
-1
2
-1