【ソースコード有】C#でデータの一覧を扱うListクラスの使い方
Listクラスは配列の優れた特徴をもちながら、要素の挿入や削除も可能なデータ構造です。その使い方について、サンプルコードとともに説明していきます。
Listクラスは配列の優れた特徴をもちながら、要素の挿入や削除も可能なデータ構造です。その使い方について、サンプルコードとともに説明していきます。
スキルアップ
2023/01/26 UP
- SE資格・スキル
- プログラミング
- システムエンジニア
プログラミングでは、データの一覧を扱いたい場面が少なくありません。そのような場合には、配列を使う方法が考えられます。しかし、配列は個々のデータに素早くアクセスできる反面、要素数が固定されているのが難点です。
そこで今回は、C#のListクラスについてみていきましょう。Listクラスは配列の優れた特徴をもちながら、要素の挿入や削除も可能なデータ構造です。その使い方について、サンプルコードとともに説明していきます。
なお、より基本からC#について知りたい場合は、こちらの記事もチェックしてみてください。
【C#入門】プログラムの基本的な文法や書き方をサンプル付きで解説
C#のListクラスとは
まずは、C#のListクラスがもつ特徴について説明します。
Listクラスは配列のようなデータ構造をもつ
「同じ型のデータが連続して配置されている」という点は、配列の重要な特徴です。これにより、データの一覧に含まれる特定の要素に、インデックス(添え字)を用いてアクセスできます。このとき、先頭の要素でも末尾の要素でも、アクセスの速度は変わりません。「要素のサイズ」×「インデックス」という計算式1つで、それぞれの要素の位置を特定できるためです。
C#のListクラスも、配列と同様にインデックスで要素にアクセスできます。これは、Listクラスが内部に配列の構造をもっているためです。
ただし、この内部的な配列は、要素数が固定されていません。必要に応じて要素の個数を増やしていくことで、データの挿入や削除もサポートしているのです。C#のListクラスは、「動的な配列」を実現するものだといえるでしょう。
Listクラスは任意の型に対応する
Listクラスは、正確には「List<T>」という型をもっています。これはC#の「ジェネリック」と呼ばれる機能によるもので、「T」の部分には要素の型を指定することが可能です。例えば「List<string>」とすれば、文字列型のデータを要素とするListクラスになります。
この点においても、Listクラスは配列と似た特徴を備えているといえるでしょう。あらゆる型の配列を作成できるのと同様に、Listクラスでも任意の型を扱えます。また、こうした再利用性の高さだけでなく、明確に型を指定するためタイプセーフ(型を通じた安全な操作が可能)である点も、Listクラスの優れた点です。
Listクラスはループで処理できる
Listクラスでは、反復処理でデータを扱うことが可能です。foreach文に対応しているため、配列と同様の自然な感覚でループを記述できるでしょう。
また、内部的な配列は要素数が固定されていませんが、現在の要素数はいつでも取得できます。そのため、必要に応じてfor文などを使用し、インデックスをループ変数とすることも可能です。
C#のListクラスの使い方
ここからは、Listクラスの使い方を具体例とともに説明していきます。Listクラスは多くの機能を備えているため、ここでは代表的な操作方法をピックアップしました。
最初に using を使用してListを使えるようにします。
using System.Collections.Generic;
作成と初期化(new/Add)
Listクラスのデータを初期化する方法は複数あります。次のようにListクラスのオブジェクトを作成し、あとから要素を追加していく方法が最もシンプルでしょう。
var myList = new List<int>(); myList.Add(123); myList.Add(456);
「new」でListを作成したあとは、このように「Add()」メソッドで要素を追加することが可能です。
または次のように、「コレクション初期化子」を用いたコンパクトな記法もあります。
var myList = new List<int> { 123, 456 };
この書き方なら、配列と同じようなイメージでListを作成できるでしょう。
なお、上の例ではint型を用いていますが、要素の型は任意に指定可能です。例えば、string型を用いるには次のようにします。
var myList = new List<string> { "テキスト1", "テキスト2" };
要素のへのアクセスとループ(foreach/for/Count)
Listの要素を順番に取り出して処理するには、foreach文によるループが最適でしょう。具体的には、次のように記述します。
foreach (var item in myList) { Console.WriteLine(item); }
配列のようにインデックスによるアクセスが可能な点も、Listの重要な特徴です。例えば次のように、for文でループしながら各要素を取り出すことができます。
for (var i=0; i<myList.Count; i++) { Console.WriteLine(myList[i]); }
Listの要素数を「Count」プロパティで取得している様子がわかるでしょう。また、インデックスを指定して要素にアクセスする際の記法も配列と変わりません。
要素の挿入と削除(Insert/Remove/RemoveAt)
Listに要素を挿入するには、「Insert()」メソッドを使用します。以下は、実際に要素の挿入を行なうプログラムの例と、その実行結果です。
サンプルコード:
var myList = new List<string> { "りんご", "ぶどう", "みかん", "いちご" }; myList.Insert(1, "バナナ"); foreach (var item in myList) { Console.WriteLine(item); }
実行結果:
りんご バナナ ぶどう みかん いちご
この例では、「りんご」から「いちご」までの4つの要素で初期化されたListに対して、インデックスで位置を指定しながら追加の要素「バナナ」を挿入しています。挿入位置からあとの要素はインデックス1つ分だけうしろに押し出され、結果として全体の要素数も1つ増えました。
こうした処理は、サイズが固定された通常の配列のみで行なうには手間がかかります。Listクラスのメリットを感じられる操作だといえるでしょう。
要素の削除は、「Remove()」または「RemoveAt()」メソッドで行なえます。次のプログラムで、動作を確認してみましょう。
サンプルコード:
var myList = new List<string> { "りんご", "ぶどう", "みかん", "いちご" }; myList.RemoveAt(1); // "ぶどう" myList.Remove("みかん"); foreach (var item in myList) { Console.WriteLine(item); }
実行結果:
りんご いちご
「RemoveAt()」メソッドは、インデックスを指定して要素を削除します。この例ではインデックス「1」に対応する「ぶどう」が削除されました。
これに対し、「Remove()」メソッドでは値を直接指定して要素を削除します。上の例では「みかん」が削除されている様子がわかるでしょう。
要素の存在確認(Contains/IndexOf)
List内に特定の要素が存在するかどうかを確認したい場合には、「Contains()」または「IndexOf()」メソッドが便利です。
「Contains()」メソッドは、値が存在する場合に「true」を返します。使い方は次のとおりです。
if (myList.Contains("バナナ")) { Console.WriteLine("バナナが見つかりました。"); } else { Console.WriteLine("バナナは見つかりませんでした。"); }
「IndexOf()」は、指定した値の要素がList内にあれば、そのインデックスを取得できるメソッドです。該当する値が存在しない場合は「-1」を返すため、次のように要素の存在確認ができます。
var index = myList.IndexOf("バナナ"); if (index != -1) { Console.WriteLine("バナナが見つかりました。"); } else { Console.WriteLine("バナナは見つかりませんでした。"); }
用途としては、重複しない値を追加したい場合は「Contains()」メソッド、既存の要素にアクセスしたい場合は「IndexOf()」メソッドが適しているでしょう。具体的には、次のような使い方です。
サンプルコード:
var myList = new List<string> { "りんご", "ぶどう", "みかん", "いちご" }; if (!myList.Contains("バナナ")) { myList.Add("バナナ"); // 存在しないので追加 } var index = myList.IndexOf("みかん"); if (index != -1) { myList[index] = "蜜柑"; // 既存の要素にアクセス } foreach (var item in myList) { Console.WriteLine(item); }
実行結果:
りんご ぶどう 蜜柑 いちご バナナ
「Contains()」メソッドが単純な存在確認のためのものなのに対し、「IndexOf()」メソッドは「検索」の意味合いが強いことがわかるでしょう。
要素の検索(FindIndex)
「FindIndex()」メソッドを使えば、「IndexOf()」メソッドよりも柔軟な検索が可能です。List内から要素を検索するための条件を、開発者が任意に指定できます。
ここで「条件」とは、要素の値を1つ受け取って、次のどちらかを返す処理のことです。
・true:検索したい値である
・false:検索したい値ではない
この条件をを用いて、「FindIndex()」メソッドは「検索したい値」をもつ要素を探し、そのインデックスを返します。要素が見つからなければ「-1」を返す点は、「IndexOf()」メソッドと同様です。
では、具体的な使用例をみてみましょう。以下は、List内から頭に「み」がつく要素を検索するプログラムです。
サンプルコード:
var myList = new List<string> { "りんご", "ぶどう", "みかん", "いちご" }; var index = myList.FindIndex(item => item.StartsWith("み")); if (index != -1) { Console.WriteLine("見つかりました:" + myList[index]); } else { Console.WriteLine("見つかりませんでした。"); }
実行結果:
見つかりました:みかん
「FindIndex()」メソッドに、「item => item.StartsWith("み")」という処理を渡している様子がわかるでしょうか。これが、検索の条件にあたる部分です。要素の値を「item」として受け取り、それが「み」で始まる文字列かどうかを判定しています。
要素の並べ替え(Sort)
要素の並べ替えは、「Sort()」メソッドで行ないます。基本的な使い方は、次のとおりです。
サンプルコード:
var myList = new List<int> { 99, 5, 1234, -10 }; myList.Sort(); foreach (var item in myList) { Console.WriteLine(item); }
実行結果:
-10 5 99 1234
Listの要素が小さい順に整列されているのがわかるでしょう。数値や文字コードの順番で並べ替えたい場合は、この方法で対応できます。
しかし、独自のルールで並び替えたいケースもあるかもしれません。その場合は、並び順を決めるための条件を、開発者が任意に指定できます。
ここで「条件」とは、2つの値を受け取り、以下のいずれかを返す処理のことです。
・負の値:1つ目の値のほうが小さい
・ゼロ:2つの値が等しい
・正の値:1つ目の値のほうが大きい
では、条件を指定する方法を具体例で確認してみましょう。以下は、文字列を文字コード順ではなく、数値とみなして並び替えるプログラムです。
サンプルコード:
var myList = new List<string> { "99", "5", "1234", "-10" }; myList.Sort((left, right) => Convert.ToInt32(left) - Convert.ToInt32(right)); foreach (var item in myList) { Console.WriteLine(item); }
実行結果:
-10 5 99 1234
「(left, right) => Convert.ToInt32(left) - Convert.ToInt32(right)」という処理が、「Sort()」メソッドに渡されているのがわかるでしょうか。これが、並び順を決めるための条件です。値を2つ受け取り、それぞれを数値に変換して比較した結果を返しています。
配列との相互変換(ToArray)
配列とListは、互いに変換できます。
配列からListへの変換は、配列を引数としてListクラスのオブジェクトを作成すれば可能です。Listから配列に変換するには、「ToArray()」メソッドを使います。
配列に対して要素の追加や挿入、削除を行ないたい場合は、一時的にListに変換するのが便利でしょう。以下は、その具体例です。
サンプルコード:
var myArray = new[] { "りんご", "みかん" }; var myList = new List<string>(myArray); myList.Add("いちご"); // 追加 myList.Insert(1, "ぶどう"); // 挿入 myList.Remove("みかん"); // 削除 myArray = myList.ToArray(); foreach (var item in myArray) { Console.WriteLine(item); }
実行結果:
りんご ぶどう いちご
通常の配列は要素数が固定されているため、同様の処理を行なうのは簡単ではありません。しかし、上記のようにListクラスの機能を用いれば、動的な配列のような操作を手軽に実現できることがわかるでしょう。
C#のListクラスとArrayクラスの違い
Listクラスを使うと、配列のようなデータ構造をより柔軟に扱えるようになることを説明してきました。特に、要素の追加や挿入、削除をともなう動的な操作を行ないたい場合には、Listクラスの機能を活用すると便利でしょう。
一方、こうした動的な操作が不要であれば、Arrayクラスを使うほうが便利な場合もあります。Arrayクラスとは、C#の配列が備える機能を表現したクラスのことです。あらゆる配列を直接扱うことができ、Listクラスと似たような操作もある程度できます。
Arrayクラスの詳しい使い方についてはこちらの記事で説明しているので、併せて参考にしてください。
【ソースコード有】C#の配列を扱いやすくするArrayクラスの使い方
C#のListクラスの特徴を理解して配列と使い分けよう
C#のListクラスは、データの一覧を扱いたい場合に便利な機能を備えたクラスです。任意の型を要素とすることができ、配列のような特徴と使い勝手も併せもちます。
要素の追加や挿入・削除といった、通常の配列で行なうには手間のかかる動的な操作が必要なシーンでは、Listクラスの利用価値は特に高いといえるでしょう。また、要素の存在確認や検索、並べ替えなどにも柔軟に対応可能です。
なお、Listクラスの機能は、既存の配列に対しても威力を発揮します。Listクラスのオブジェクトは、配列との相互変換が可能なためです。これまで通常の配列で行なっていた処理を、配列よりも機能が豊富だからといって、あわててListクラスで作り直す必要はありません。Listクラスと配列は、目的に応じて使い分けるのがよいでしょう。