C#の「Listクラス」は、要素の追加と削除が自由にできる「動的配列」です。配列とは異なり、状況に応じて臨機応変に対応しやすいことが魅力。しかしC#のListにはさまざまなメソッドがあるため、うまく使いこなすのが難しいこともあるでしょう。本記事ではC#のListクラスの概要と使い方、応用テクニックについてサンプルプログラム付きで解説します。
目次
C#の「Listクラス」は、同じデータ型の変数をまとめて格納できるクラスです。配列とは異なり、生成後も要素の追加と削除ができるうえに、「ジェネリッククラス」なので、さまざまなデータ型に対応できることが魅力。プログラム内でデータ量が大きく変動する場合は、Listクラスを使うのが効率的です。Listクラスの基本的な仕組みを確認しておきましょう。
var list = new List<int>(); |
Listクラスは上記のように宣言します。変数のデータ型は「var」による型推論に任せるのが基本。オブジェクトの生成は配列同様にnew演算子を使用しますが、「List<型名>」の形式でジェネリックの型名を指定する点が異なります。最後に「()」でコンストラクタを呼び出せば、空のListクラスの生成が完了です。Listの初期化方法については改めて解説します。
C#のListクラスは、いわば動的な変更を可能にした配列です。まずはListを使ったプログラムの全体像を把握したうえで、Listクラスの基本的な使い方を確認していきましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3}; // 各要素にインデックスでアクセスする Console.WriteLine(nums[0]); Console.WriteLine(nums[1]); Console.WriteLine(nums[2]); // すべての要素にアクセスする foreach(var n in nums) { Console.WriteLine(n); } Console.ReadKey(); } } |
//実行結果 |
1
2 3 1 2 3 |
Listクラスは宣言と生成の方法が配列と異なりますが、そのほかは配列と同じように扱えることがポイント。本章では、下記3つの観点からListの基本的な使い方を解説します。
Listクラスをインスタンス化する方法は前述したとおりですが、インスタンス化しただけでは中身が入っていない「空のリスト」となります。データ数が少ない場合は、下記のサンプルプログラムのように、インスタンス化と初期化を同時に行いましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var iList = new List<int>() {1, 2, 3}; // float型Listの生成と初期化を行う var fList = new List<float>() {10.0f, 20.0f, 30.0f}; // string型Listの生成と初期化を行う var sList = new List<string>() {“abc”, “def”, “ghi”}; // int型Listのすべての要素を表示する foreach(var v in iList) { Console.WriteLine(v); } // float型Listのすべての要素を表示する foreach(var v in fList) { Console.WriteLine(v); } // stringh型Listのすべての要素を表示する foreach(var v in sList) { Console.WriteLine(v); } Console.ReadKey(); } } |
//実行結果 |
1
2 3 10 20 30 abc def ghi |
コンストラクタ呼び出しの「()」のあとに、「{}」の初期化リストで値を設定するとインスタンス化と初期化を同時に行えます。すぐに初期化する必要がないときや設定値が多い場合は、後述する「Addメソッド」で個別に設定しても構いません。
リスト内の任意の要素にアクセスしたいときは、配列と同じように「[]」でインデックスを指定しましょう。Listに格納しているデータを取得するサンプルプログラムを紹介します。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3, 4, 5}; // Listの要素にアクセスして値を表示する Console.WriteLine(nums[0]); Console.WriteLine(nums[1]); Console.WriteLine(nums[2]); Console.WriteLine(nums[3]); Console.WriteLine(nums[4]); Console.ReadKey(); } } |
//実行結果 |
1
2 3 4 5 |
上記のように、配列と同じ感覚で要素にアクセスできます。ただしListの範囲外の場所にアクセスすると、「ArgumentOutOfRangeException」例外が出るので注意が必要です。
Listに格納したすべての要素に対して、更新や表示などの処理を行いたい場合は、「forループ」や「foreach」でアクセスしましょう。サンプルプログラムは下記のとおりです。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3}; // 通常のforループで全要素にアクセスする for(int i = 0; i < nums.Count; i++) { Console.WriteLine(nums[i]); } // foreachで全要素にアクセスする foreach(var n in nums) { Console.WriteLine(n); } Console.ReadKey(); } } |
//実行結果 |
1
2 3 1 2 3 |
全要素へのアクセスも基本的には配列と変わりません。ループ内でインデックス値を使用する必要がない場合は、「foreach」を使うとソースコードが短くなるのでおすすめです。
Listクラスの基本的な使い方は配列とあまり変わりません。しかしListを便利に使いこなすためには、Listに搭載されているさまざまなメソッドを理解しておくことが重要です。本章では下記4種類に分類して、Listクラスのメソッドと応用法を紹介します。
C#のListクラスの魅力は、インスタンス後も自由に要素を追加できること。Listクラスでは下記2つのメソッドで要素を追加できます。
メソッド名 | Add |
引数 | 追加する要素 |
戻り値 | なし |
「Add」は末尾に要素を追加するためのメソッド。要素の順番や追加する位置を気にする必要がない場合は、Addメソッドを使うのがおすすめです。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // string型Listを生成する var texts = new List<string>(); // Addメソッドでひとつずつ要素を追加する texts.Add(“abc”); texts.Add(“def”); texts.Add(“ghi”); texts.Add(“jkl”); texts.Add(“mno”); // すべての要素を表示する foreach(var str in texts) { Console.WriteLine(str); }
Console.ReadKey(); } } |
//実行結果 |
abc
def ghi jkl mno |
Addメソッドを活用すれば、プログラムの処理に応じて適切なサイズの配列が使えるのですが、List変数と追加要素のデータ型は一致する必要があります。
メソッド名 | Insert |
第1引数 | 挿入するインデックス |
第2引数 | 挿入する要素 |
戻り値 | なし |
「Insert」は指定した位置に要素を追加するためのメソッド。挿入する位置が重要な場合は、Insertメソッドで正確な位置を指定しましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // string型Listの生成と初期化を行う var texts = new List<string>() {“a”, “b”, “d”, “e”, “f”}; // Insertメソッドで2番目に”c”を挿入する texts.Insert(2, “c”); // すべての要素を表示する foreach(var str in texts) { Console.WriteLine(str); }
Console.ReadKey(); } } |
//実行結果 |
a
b c d e f |
Insertメソッドの第一引数には「上記のプログラムでは、「”b”」と「”d”」の間に「”c”」を挿入する必要があるため、Insertメソッドの第一引数に。
C#のListクラスの魅力は、いつでも要素を削除できることです。Listクラスでは下記4つのメソッドで、さまざまな方法で要素を削除できます。
メソッド名 | Remove |
引数 | 削除したい値 |
戻り値 | 削除の成否 |
「Remove」は、リスト内の指定した値が格納されている要素を削除するためのメソッドになります。削除したい要素のインデックスがわからない場合は、Removeメソッドを使用しましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {10, 20, 30, 40, 50}; // Removeメソッドで”10″と”50″を削除する nums.Remove(10); nums.Remove(50); // すべての要素を表示する foreach(var n in nums) { Console.WriteLine(n); } // 削除できなかった場合はfalseが返る if(!nums.Remove(0)) { Console.WriteLine(“要素\”0\”を削除できませんでした”); } Console.ReadKey(); } } |
//実行結果 |
20
30 40 要素”0″を削除できませんでした |
Removeメソッドの第1引数に指定した値がリスト内に存在する場合は、そのまま要素が削除されます。値が重複している場合は、インデックスが最も小さいものしか削除されないので注意が必要です。
なおリスト内に存在しない値を指定した場合は、戻り値として「false」が返ります。上記のサンプルプログラムでは、if文で判定してメッセージを表示しました。エラー処理の参考にしてみてください。
メソッド名 | RemoveAll |
引数 | 削除条件を定義するラムダ式 |
戻り値 | 削除した要素数 |
「RemoveAll」は、指定した条件に当てはまる要素をすべて削除するためのメソッド。複数の要素をまとめて削除したいときや、値ではなく条件を指定したい場合はRemoveAllメソッドの使用がおすすめです。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { int count; // int型Listの生成と初期化を行う var nums = new List<int>() {10, 10, 20, 30, 40, 50, 50, 50}; // Removeメソッドで”10″を削除する count = nums.RemoveAll(x => x == 10); Console.WriteLine(count + “個の\”10\”を削除しました”); // Removeメソッドで”50″を削除する count = nums.RemoveAll(x => x == 50); Console.WriteLine(count + “個の\”50\”を削除しました”); // すべての要素を表示する foreach(var n in nums) { Console.WriteLine(n); } Console.ReadKey(); } } |
//実行結果 |
2個の”10″を削除しました
3個の”50″を削除しました 20 30 40 |
RemoveAllメソッドは引数に注意が必要です。ここには「ラムダ式」を記載する必要があります。ラムダ式は「簡易的な関数」を意味し、具体的な処理内容を記載するものです。ここでは「x => x == 10」すなわち「値をxとしたとき、xが10の要素を削除する」としました。ラムダ式は慣れないと使いづらいですが、「x =>」の部分は「ある要素の値をxと仮定する」ことを意味します。
後半の「x == 10」の部分が条件式で、ここが「真」になるものが削除されるということです。なお戻り値は削除個数なので、必要に応じて活用しましょう。
メソッド名 | RemoveAt |
引数 | 削除したいインデックス |
戻り値 | なし |
「Remove」は、インデックスを直接指定して要素を削除するためのメソッドです。削除すべき要素のインデックスがわかっている場合は、RemoveAtメソッドを使うのが一般的。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {10, 20, 30, 40, 50}; // Removeメソッドで0番目の要素”10″を削除する nums.RemoveAt(0); // Removeメソッドで3番目の要素”50″を削除する nums.RemoveAt(3); // すべての要素を表示する foreach(var n in nums) { Console.WriteLine(n); } Console.ReadKey(); } } |
//実行結果 |
20
30 40 |
先ほどのRemoveは値を指定するため、インデックスの「検索」が必要でした。RemoveAtなら指定したインデックスを直接削除できるので、検索する必要がなく速度的に有利です。Listのサイズ以上のインデックスを指定すると、「ArgumentOutOfRangeException」例外が発生します。より安全なコードを書くためには、例外処理をしておくほうがいいでしょう。
メソッド名 | RemoveRange |
第1引数 | 削除を開始するインデックス |
第2引数 | 削除する要素数 |
戻り値 | なし |
「RemoveRange」は、インデックスの範囲を指定して要素を削除できるメソッドです。複数の要素をまとめて削除したい場合は、RemoveRangeメソッドを活用してみましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {10, 20, 30, 40, 50}; // RemoveRangeメソッドで1番目から3つの要素を削除する nums.RemoveRange(1, 3); // すべての要素を表示する foreach(var n in nums) { Console.WriteLine(n); } Console.ReadKey(); } } |
//実行結果 |
10
50 |
上記のプログラムでは、1番目のインデックスから3個の要素、つまり「20」「30」「40」を削除しています。範囲外を指定すると「ArgumentException」と、例外が出るので要注意。
メソッド名 | Clear |
引数 | なし |
戻り値 | なし |
「Clear」は、リスト内の全要素を削除して空にするためのメソッド。リストの再構成や再利用を行うときは、Clearメソッドで全削除してから使うのがおすすめです。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {10, 20, 30, 40, 50}; // Listの要素数を表示する Console.WriteLine(“Clear前:” + nums.Count); // RemoveRangeメソッドで1番目から3つの要素を削除する nums.Clear(); // Listの要素数を表示する Console.WriteLine(“Clear後:” + nums.Count); Console.ReadKey(); } } |
//実行結果 |
Clear前:5
Clear後:0 |
上記のプログラムのように、Clearメソッドの使用後は要素数がゼロになっており、リストが空になっていることがわかります。リストのインスタンス自体は、削除されません。
C#のListクラスでは、要素を検索するための便利なメソッドも揃っています。下記のようなメソッドを活用すれば、リストの検索で困ることはないでしょう。
「Countプロパティ」は、Listの要素数を確認するためのものです。メソッドではなくプロパティなので、下記のように呼び出すだけで情報を取得できます。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9}; // CountでListの要素数を表示する Console.WriteLine(nums.Count); Console.ReadKey(); } } |
//実行結果 |
9 |
Countプロパティはリストのサイズを把握する必要があるとき、たとえばforループの条件式の設定時などに活用すると、正確な数値がわかるので便利です。
メソッド名 | Find |
引数 | 検索条件を定義するラムダ式 |
戻り値 | 検出した要素 |
「Find」は、特定の条件に合致する値がリスト内に存在するか調べるためのメソッド。検索条件をラムダ式で指定すると、それに合致する最初の要素が戻り値として返されます。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // Findで最初の「偶数」を検索する int result = nums.Find(n => n % 2 == 0); // 検索結果を表示する Console.WriteLine(“最初の偶数:” + result); // Findで最初の「5の倍数」を検索する result = nums.Find(n => n % 5 == 0); // 検索結果を表示する Console.WriteLine(“最初の5の倍数:” + result); Console.ReadKey(); } } |
//実行結果 |
1 2 3 4 5 6 7 8 9
最初の偶数:2 最初の5の倍数:5 |
ラムダ式については前述しましたが、「n =>」の変数宣言のあとに「n % 5 == 0」という条件を指定しています。ラムダ式には慣れが必要なので、いろいろと試してみましょう。
メソッド名 | FindAll |
引数 | 検索条件を定義するラムダ式 |
戻り値 | 検出した要素のリスト |
「FindAll」は、特定の条件に合致するすべての値を検出するためのメソッドです。先ほどのFindメソッドとは異なり、リスト内で合致するすべての要素がリスト形式で返されます。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // FindAllですべての「偶数」を検索する var list = nums.FindAll(n => n % 2 == 0); // 検索結果を表示する Console.Write(“すべての偶数:”); foreach(var n in list) Console.Write(n + ” “); Console.ReadKey(); } } |
//実行結果 |
1 2 3 4 5 6 7 8 9
すべての偶数:2 4 6 8 |
FindAllメソッドでは結果がリスト形式で返りますが、検出されなかった場合は空リストとなります。ラムダ式の内容を変更して、どのような結果になるか確認してみてください。
メソッド名 | FindIndex |
引数 | 検索条件を定義するラムダ式 |
戻り値 | 検出した要素のインデックス |
「FindIndex」は、指定した条件の合致する要素のインデックスを取得するためのメソッド。要素の値そのものではなくインデックスを知りたい場合、FindIndexがおすすめです。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { int result; // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // FindIndexで「奇数値」のインデックスを検索する result = nums.FindIndex(n => n % 2 != 0); // 検索結果を表示する Console.WriteLine(“最初の奇数のインデックス:” + result); // FindIndexで「0」のインデックスを検索する result = nums.FindIndex(n => n == 0); // 条件に合致する値が検出されなかった場合は「負の値」が返る if(result < 0) { Console.WriteLine(“要素が見つかりませんでした”); } Console.ReadKey(); } } |
//実行結果 |
1 2 3 4 5 6 7 8 9
最初の奇数のインデックス:0 要素が見つかりませんでした |
上記のプログラムのように、条件に合致する要素がなかった場合は「負の値」が返ってきます。情報を正確に判断する必要があるときは、戻り値のチェックを行うようにしましょう。
メソッド名 | Contains |
引数 | 検索したい値 |
戻り値 | 指定した要素が存在するか |
「Contains」は、指定した値がリスト内に存在するかを調べるメソッドです。ラムダ式を使用した「条件」ではなく、「特定の値があるか」をチェックしたい場合に使います。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { int input = -1; // int型Listの生成と初期化を行う var nums = new List<int>() {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // 検索したいデータをユーザーに入力してもらう Console.Write(“検索したい値を入力してください:”); if( !int.TryParse(Console.ReadLine(), out input)) { // TryParseの戻り値がfalseの場合は入力値が不正 Console.WriteLine(“不正な入力値です”); } // Containsで入力値がListに含まれているか検索する if(nums.Contains(input)) { Console.WriteLine(input + “が検出されました”); } else { Console.WriteLine(input + “は検出されませんでした”); } Console.ReadKey(); } } |
//実行結果 |
2 3 5 7 11 13 17 19 23 29
検索したい値を入力してください:17 17が検出されました |
上記のプログラムでは、ユーザーから整数値の入力を受け付けて、整合性のチェックも行っています。Containsメソッドの戻り値の扱い方も含めて、ぜひ参考にしてみてください。
メソッド名 | IndexOf |
引数 | 検索したい値 |
戻り値 | 検出した要素のインデックス |
「IndexOf」は指定した値がリスト内に存在するかを調べ、そのインデックスを返すメソッドです。存在の有無に加えてインデックスも取得したい場合、IndexOfを使いましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // 検索したいデータをユーザーに入力してもらう int input; Console.Write(“検索したい値を入力してください:”); int.TryParse(Console.ReadLine(), out input); // Containsで入力値がListに含まれているか検索する int index = nums.IndexOf(input); if(index > 0) { Console.WriteLine(input + “のインデックスは” + index + “です”); } else { Console.WriteLine(input + “が見つかりませんでした”); } Console.ReadKey(); } } |
//実行結果 |
2 3 5 7 11 13 17 19 23 29
検索したい値を入力してください:11 11のインデックスは4です |
こちらも値が検出されなかった場合は「負の値」が返ってくるので、if文を使えば検出の有無を判定できます。なお「int.TryParse」は、文字列をint型変数に変換するメソッドです。
C#のListクラスには、要素をソートするための機能も揃っています。リスト内の要素を条件に従って並べ替えたい場合は、下記3種類のメソッドやテクニックを活用しましょう。
メソッド名 | Sort |
引数 | なし |
戻り値 | なし |
「Sort」はリストを「昇順」で並べ替えるためのメソッド。引数や戻り値なしで、呼び出すだけでソートが完了して便利です。昇順ソートが必要な場合はぜひ活用しましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 9, 3, 5, 4, 8, 7, 2, 6}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // Listを昇順で並べ替える nums.Sort(); // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); Console.ReadKey(); } } |
//実行結果 |
1 9 3 5 4 8 7 2 6
1 2 3 4 5 6 7 8 9 |
ただし上記のプログラムでは、昇順ソートしかできないので要注意。なおSortメソッドには、ラムダ式で条件を指定できるタイプもありますが、こちらは後ほど改めて解説します。
メソッド名 | Reverse |
引数 | なし |
戻り値 | なし |
「Reverse」はリストを「逆順」に並べ替えるためのメソッド。何らかの理由でリスト内の並びを反対にしたい場合は、Reverseメソッドを呼び出すだけでいいので便利です。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // Listを逆順に並べ替える nums.Reverse(); // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); Console.ReadKey(); } } |
//実行結果 |
1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 |
なお、先ほどのSortメソッドを呼び出したあとでReverseメソッドを使うと、降順に並べ替えることも可能。ラムダ式でのソートが、よくわからない場合の代替手段となります。
メソッド名 | Sort |
引数 | ソート処理を定義するラムダ式 |
戻り値 | なし |
「Sort」の引数にラムダ式を設定すると、リストを自由にソートできます。ここではリストを降順で並べ替える手順を、下記のサンプルプログラムを交えて確認していきましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 9, 3, 5, 4, 8, 7, 2, 6}; // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // Listを降順で並べ替える nums.Sort((a, b) => b – a);
// すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); Console.ReadKey(); } } |
//実行結果 |
1 9 3 5 4 8 7 2 6
9 8 7 6 5 4 3 2 1 |
重要なポイントはSortの引数が「(a, b) => b – a」となっていること。「リスト内の左側と右側の要素をそれぞれa・bと仮定し、bがaより大きい場合に並べ替える」という意味です。なお「b – a」の部分は「b.CompareTo(a)」に置き換えても構いません。CompareToメソッドは「左側の変数から右側の変数を引いた結果」を返すので、「b – a」と同じことです。
Listクラスのインスタンスは「参照型変数」となります。参照型変数をメソッドに引数として渡した場合は、シャローコピーが行われることに要注意。メソッド内部でリストの内容が変更されると、呼び出し元のリストも書き換えられます。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var nums = new List<int>() {1, 9, 3, 5, 4, 8, 7, 2, 6}; // すべての要素を表示する Console.Write(“メソッド呼び出し前:”); foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); // メソッドを呼び出す Use(nums); // すべての要素を表示する Console.Write(“メソッド呼び出し後:”); foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); Console.ReadKey(); } static void Use(List<int> list) { // Listを昇順で並べ替える list.Sort(); // すべての要素を表示する Console.Write(“メソッド内:”); foreach(var n in list) Console.Write(n + ” “); Console.WriteLine(); } } |
//実行結果 |
メソッド呼び出し前:1 9 3 5 4 8 7 2 6
メソッド内:1 2 3 4 5 6 7 8 9 メソッド呼び出し後:1 2 3 4 5 6 7 8 9 |
上記のプログラムでは、自作のUseメソッド内部で「list」の昇順ソートを行っています。しかしMainメソッドの「nums」も変更されています。numsをそのまま保持しておきたい場合、これは予期せぬ結果となってしまうでしょう。
呼び出し元のリストが書き換えられるのを防ぐ場合は、ディープコピーを行ってからメソッドへ引き渡す必要があります。下記のように、新しいインスタンスをコピーコンストラクタで生成すると、ディープコピーを行うことが可能。
// コピーコンストラクタで新たなListにディープコピーする
var copy = new List<int>(nums); // メソッドの呼び出し時にコピーしたListを引き渡す Use(copy); |
メソッドの呼び出し時にコピーしたインスタンスを引き渡すと、メソッド内でどのような変更を加えても元のリストは変更されません。これはほかのクラスでも同様なので、予期せぬバグを減らすために意識しておきたいポイントです。
リストは「配列の発展形」とも呼べるクラスですが、それぞれを相互に変換できるとさらに便利です。配列からリスト、リストから配列への変換は簡単な手順で行えます。本章では下記2つのパターンに分けて、変換方法を確認していきましょう。
配列からリストに変換したい場合は、リストのインスタンス生成時にコピーコンストラクタに配列を引き渡しましょう。サンプルプログラムを紹介します。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型配列の生成と初期化を行う int[] nums = {1, 9, 3, 5, 4, 8, 7, 2, 6}; // int型リストの生成時にコピーコンストラクタで変換する var list = new List<int>(nums); // すべての要素を表示する foreach(var n in list) Console.Write(n + ” “); Console.WriteLine(); Console.ReadKey(); } } |
//実行結果 |
1 9 3 5 4 8 7 2 6 |
「list」の要素は「nums」のディープコピーとなるため、「list」にあとから変更を加えても元の配列「nums」の内容が何らかの影響を受けることはありません。
リストから配列への変換を行いたい場合は、「ToArrayメソッド」を活用しましょう。下記のサンプルプログラムの手順で簡単に行えます。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { // int型Listの生成と初期化を行う var list = new List<int>() {1, 9, 3, 5, 4, 8, 7, 2, 6}; // ToArrayでint型配列に変換する int[] nums = list.ToArray(); // すべての要素を表示する foreach(var n in nums) Console.Write(n + ” “); Console.WriteLine(); Console.ReadKey(); } } |
//実行結果 |
1 9 3 5 4 8 7 2 6 |
「nums」の要素は「list」のディープコピーとなるため、「nums」の内容をあとから変更しても、元のリスト「list」は影響を受けません。
C#では二次元配列や三次元配列など、多次元配列を生成することができます。Listクラスには基本的に一次元のみで、二次元以上のクラスは用意されていません。しかし下記のサンプルプログラムのように、リストのなかにリストを作成すれば多次元配列を再現できます。
//サンプルプログラム |
using System;
using System.Collections.Generic; class Sample { static void Main(string[] args) { int numX = 10; // 一次元方向の要素数 int numY = 5; // 二次元方向の要素数 int count = 0; // 要素カウント用変数 // ジェネリクスの型名をListにして二次元リストを宣言する var list = new List<List<int>>(); // 二次元目の要素をnumY個生成する for(int i = 0; i < numY; i++) { list.Add(new List<int>()); } // 先ほど生成した二次元目に一次元目を追加していく foreach(var y in list) { // 一次元目の要素をnumX個生成する for(int i = 0; i < numX; i++) { y.Add(count++); // 要素のデータは順番に1ずつ増やす } } // 二重foreachループで二次元リストの全要素を表示する foreach(var y in list) { foreach(var x in y) { Console.Write(“{0,3:d}”, x); // 書式設定で3桁表示にする } Console.WriteLine(); // 改行する }
Console.WriteLine(); // 改行する // 適当な場所の要素を「-1」にする // イメージを掴むためにいろいろ変更してみてください list[1][2] = -1; list[2][8] = -1; list[3][6] = -1; list[4][0] = -1; // 改めてすべての要素を表示する foreach(var y in list) { foreach(var x in y) { Console.Write(“{0,3:d}”, x); } Console.WriteLine(); } Console.ReadKey(); } } |
//実行結果 |
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 -1 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -1 29 30 31 32 33 34 35 -1 37 38 39 -1 41 42 43 44 45 46 47 48 49 |
詳細は上記のプログラム処理を、ひとつずつ追って確認してみてください。大まかな流れとしては、型名にリストを指定したListインスタンスを作成し、二次元目→一次元目の順番で要素を追加していきます。三次元以上の多次元配列も、同じような手順で作成可能です。
C#にはListのほかに、「ArrayList」というクラスも存在します。しかしListとArrayListには大きな違いがあるため、基本的にはListクラスを使用するのがおすすめです。サンプルプログラムで両者の違いを確認しておきましょう。
//サンプルプログラム |
using System;
using System.Collections.Generic; using System.Collections; class Sample { static void Main(string[] args) { string str; // ArrayListの生成と要素の追加を行う var listA = new ArrayList(); listA.Add(“a”); // 文字列を追加する listA.Add(123); // 整数を追加する // Listの生成と要素の追加を行う var listB = new List<string>(); listB.Add(“a”); // 文字列だけ追加できる // listB.Add(123); // ←整数は追加できない // ArrayListから要素を取り出す // str = listA[0]; // ←キャストなしではエラーが出る str = (string)listA[0]; // 要素の取り出し時はキャストが必要 // Listから要素を取り出す str = listB[0]; // 要素の取り出し時にキャストは必要 } } |
Listとは異なり、ArrayListには複数のデータ型をまとめて格納可能。しかし要素の取り出し時にはキャストが必要なこと、Findなど使えないメソッドがある点が異なります。Listクラスのほうが、便利な点が多いといえるでしょう。
C#のListクラスは基本的に配列と同じように使えるうえに、便利なメソッドが搭載されているため、うまく活用すると高度で効率的なプログラミングが可能。ラムダ式のような難しい知識が必要なメソッドもありますが、ひとつずつ着実に慣れて使いこなしていきましょう。
2024.06.17
子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介
#プログラミングスクール
2022.01.06
【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説
#プログラミングスクール
2024.01.26
【未経験でも転職可】30代におすすめプログラミングスクール8選!
#プログラミングスクール
2024.01.26
初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説
#JAVA
2024.01.26
忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説
#プログラミングスクール
2022.01.06
【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介
#プログラミングスクール
2024.01.26
【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説
#プログラミングスクール
2024.01.26
【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説
#プログラミングスクール
2024.01.26
【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説
#プログラミングスクール
2024.01.26
【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説
#プログラミングスクール