連載
» 2017年03月01日 05時00分 公開

.NET TIPS:Listに要素を追加/挿入するには?[C#/VB]

List<T>オブジェクト作成時にList<T>コレクションに要素を追加する方法や、Add/AddRangeメソッドやInsert/InsertRangeメソッドを使いList<T>コレクションに要素を追加する方法を紹介する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
「.NET TIPS」のインデックス

連載目次

 コレクションで最もよく使うのはList<T>(C#)/List(Of T)(VB)クラス(System.Collections.Generic名前空間)であろう(以降、型引数はC#での表記だけとさせていただく)。本稿では、List<T>オブジェクトに要素を追加する方法を整理して解説する。まとめて追加できたり任意の場所に挿入できたりと、多くのバリエーションがあるのだ。

 なお、List<T>クラスは.NET Framework 2.0で導入されたものだが、本稿はそれ以降の内容も含んでいる。サンプルコードをそのまま試すには、Visual Studio 2015(またはそれ以降)が必要である。

Listに要素を追加するには?

 大きく分けて3通りある。初期化時に要素を与える方法、Addメソッド/AddRangeメソッドを使って末尾に追加する方法、そしてInsertメソッド/InsertRangeメソッドを使って任意の場所に挿入する方法である(次のコード)。

// 1. 初期化時に要素を与える方法
// a) コンストラクタに既存のコレクションを与える
IEnumerable<T> anotherCollection = ……省略……;
var list1a = new List<T>(anotherCollection);
// b) 初期化と一緒に初期値を書く(T1〜T3はT型のオブジェクト)
var list1b = new List<T>{ T1, T2, T3, };

// 2. Addメソッド/AddRangeメソッドを使う方法
var list2 = new List<T>();
list2.Add(T1); // 末尾に追加
list2.AddAddRange(anotherCollection); // 末尾に複数を追加

// 3. Insertメソッド/InsertRangeメソッドを使う方法
list2.Insert(0, T2); // 先頭に挿入
list2.Insert(list2.Count, T3); // 末尾に追加
list2.InsertRange(2, anotherCollection); // 3番目から複数を挿入

' 1. 初期化時に要素を与える方法
' a) コンストラクタに既存のコレクションを与える
Dim anotherCollection As IEnumerable(Of T) = ……省略……
Dim list1a = new List(Of T)(anotherCollection)
' b) 初期化と一緒に初期値を書く(T1〜T3はT型のオブジェクト)
Dim list1b = new List(Of T) From { T1, T2, T3 }

' 2. Addメソッド/AddRangeメソッドを使う方法
Dim list2 = new List(Of T)()
list2.Add(T1) ' 末尾に追加
list2.AddAddRange(anotherCollection) ' 末尾に複数を追加

' 3. Insertメソッド/InsertRangeメソッドを使う方法
list2.Insert(0, T2) ' 先頭に挿入
list2.Insert(list2.Count, T3) ' 末尾に追加
list2.InsertRange(2, anotherCollection) ' 3番目から複数を挿入

Listに要素を追加する方法(上:C#、下:VB)

末尾に追加するにはAddメソッド/AddRangeメソッドを、途中に挿入するにはInsertメソッド/InsertRangeメソッドを使う。
なお、Insertメソッド/InsertRangeメソッドは第1引数で指定したインデックスの位置に要素を挿入するものだが、最後のインデックス+1(=Listに含まれている要素数)も指定できる。これには、例えばソート順に従って位置を決めて要素を挿入する処理を書くときに、途中に挿入する場合と末尾に追加する場合とを区別しなくてよいというメリットがある。


初期化時に要素を与える例

 実際に、stringオブジェクトを格納するList<string>コレクションで動作を確認してみよう。

 次のコードは、初期化時に要素を与える方法を使ったコンソールアプリの例だ。後のサンプルコードでも使うため、「DisplayItems<T>」メソッドを切り出してある。

 1番目の既存のコレクションをコンストラクタに与える方法は、実際のコードでもよく使われる。2番目のコレクション初期化子を使う方法は、製品のコードではあまり見かけないかもしれないが、ユニットテストのコードではよく使われる。

using System;
using System.Collections.Generic;
using static System.Console;

class Program
{
  static void DisplayItems<T>(string msg, IEnumerable<T> collection)
  {
    WriteLine($"{msg}: {string.Join(", ", collection)}");
  }

  static void Main(string[] args)
  {
    // あらかじめコレクションを用意しておく
    IEnumerable<string> strings = new [] { "aaa", "bbb", "ccc", };
    DisplayItems(nameof(strings), strings);
    // 出力:strings: aaa, bbb, ccc

    // 初期化時に既存のコレクションを渡す
    var list1 = new List<string>(strings);
    DisplayItems(nameof(list1), list1);
    // 出力:list1: aaa, bbb, ccc

    // 初期化と一緒に初期値を書く(コレクション初期化子)
    var list2 = new List<string> { "123", "456", "789", };
    DisplayItems(nameof(list2), list2);
    // 出力:list2: 123, 456, 789

#if DEBUG
    ReadKey();
#endif
  }
}

Imports System.Console

Module Module1
  Sub DisplayItems(Of T)(msg As String, collection As IEnumerable(Of T))
    WriteLine($"{msg}: {String.Join(", ", collection)}")
  End Sub

  Sub Main()
    ' あらかじめコレクションを用意しておく
    Dim strings As IEnumerable(Of String) = New String() {"aaa", "bbb", "ccc"}
    DisplayItems(NameOf(strings), strings)
    ' 出力:strings: aaa, bbb, ccc

    ' 初期化時に既存のコレクションを渡す
    Dim list1 = New List(Of String)(strings)
    DisplayItems(NameOf(list1), list1)
    ' 出力:list1: aaa, bbb, ccc

    ' 初期化と一緒に初期値を書く(コレクション初期化子)
    Dim list2 = New List(Of String) From {"123", "456", "789"}
    DisplayItems(NameOf(list2), list2)
    ' 出力:list2: 123, 456, 789

#If DEBUG Then
    ReadKey()
#End If
  End Sub
End Module

List<string>コレクションの初期化時に要素を与える方法(上:C#、下:VB)
配列はIEnumerable<T>インタフェースを実装しているので、文字列の配列はIEnumerable<string>型の「strings」変数へ代入できる。List<T>クラスのコンストラクタ引数には、IEnumerable<T>型のコレクションを渡せる。そのことを強調するため、ここでは文字列配列をIEnumerable<string>型の変数にいったん代入している。文字列配列をList<T>クラスのコンストラクタ引数に直接渡しても構わない。
2番目のコレクション初期化子を使う方法は、「.NET TIPS:構文:コレクションのインスタンス化と同時に要素を追加するには?[C#/VB]」をご覧いただきたい(この記事では、strings変数に代入している右辺の「配列初期化子」についても解説している)。
C#コードの冒頭から3行目にある「using static System.Console;」という書き方は、Visual Studio 2015からのものだ。詳しくは、「.NET TIPS:構文:クラス名を書かずに静的メソッドを呼び出すには?[C# 6.0]」をご覧いただきたい。同様な機能がVBには以前から備わっており、「.NET TIPS:VB.NETでクラス名を省略してメソッドや定数を利用するには?」で解説している。
WriteLineメソッドの引数に出てくる先頭に「$」記号が付いた文字列については、「.NET TIPS:数値を右詰めや0埋めで文字列化するには?[C#、VB]」の後半(「C# 6.0/VB 14で追加された補間文字列機能を使用する」)を見てほしい。
また、「Main」メソッド末尾にReadKeyメソッドを置く意味は、「.NET TIPS:Visual Studioでコンソール・アプリケーションのデバッグ実行時にコマンド・プロンプトを閉じないようにするには?」をご覧いただきたい。
「DisplayItems」メソッドの引数に出てくるnameof/NameOfについては、「.NET TIPS:構文:文字列にクラス名などを間違えないようにコーディングするには?[C# 6.0]」で解説している。

Addメソッド/AddRangeメソッドを使う例

 Addメソッドは、List<T>コレクションの末尾に1個だけ要素を追加する。AddRangeメソッドは、既存のコレクションに格納されている複数の要素をList<T>コレクションの末尾に追加する(次のコード)。

 foreach/For Eachループの中で既存のコレクションから要素を取り出してはAddメソッドで追加していくというコードをよく見かけるが、次のコードの最後の例のようにLINQ拡張メソッドを併用してAddRangeメソッドで書けることも多い。

var list2 = new List<string> { "123", "456", "789", };
DisplayItems(nameof(list2), list2);
// 出力:list2: 123, 456, 789

// Addメソッド
list2.Add("X");
list2.Add("Y");
list2.Add("Z");
DisplayItems(nameof(list2), list2);
// 出力:list2: 123, 456, 789, X, Y, Z

// AddRangeメソッド
list2.AddRange(strings); // 「strings」変数は前出
DisplayItems(nameof(list2), list2);
// 出力:list2: 123, 456, 789, X, Y, Z, aaa, bbb, ccc

// 次のdicから"D1"と"D2"を取り出してlist2に追加したい
Dictionary<int, string> dic = new Dictionary<int, string>
{
  { 1, "D1"}, { 2, "D2"},
};
// foreachでAddメソッドを呼び出すコードをよく見かけるが、AddRangeメソッドで書ける
// (冒頭に「using System.Linq;」が必要)
list2.AddRange(dic.Select(kv => kv.Value));
DisplayItems(nameof(list2), list2);
// 出力: list2: 123, 456, 789, X, Y, Z, aaa, bbb, ccc, D1, D2

Dim list2 = New List(Of String) From {"123", "456", "789"}
DisplayItems(NameOf(list2), list2)
' 出力:list2: 123, 456, 789

' Addメソッド
list2.Add("X")
list2.Add("Y")
list2.Add("Z")
DisplayItems(NameOf(list2), list2)
' 出力:list2: 123, 456, 789, X, Y, Z

' AddRangeメソッド
list2.AddRange(strings) '「strings」変数は前出
DisplayItems(NameOf(list2), list2)
' 出力:list2: 123, 456, 789, X, Y, Z, aaa, bbb, ccc

' 次のdicから"D1"と"D2"を取り出してlist2に追加したい
Dim dic As Dictionary(Of Integer, String) = New Dictionary(Of Integer, String) _
From {
  {1, "D1"}, {2, "D2"}
}
' For EachでAddメソッドを呼び出すコードをよく見かけるが、AddRangeメソッドで書ける
list2.AddRange(dic.Select(Function(kv) kv.Value))
DisplayItems(NameOf(list2), list2)
' 出力: list2: 123, 456, 789, X, Y, Z, aaa, bbb, ccc, D1, D2

Addメソッド/AddRangeメソッドを使う例(上:C#、下:VB)
Dictionaryクラスを初期化しているコードについては、「.NET TIPS:Dictionaryクラスを簡単に初期化するには?[C# 3.0]」をご覧いただきたい。

Insertメソッド/InsertRangeメソッドを使う例

 Insertメソッドは、List<T>コレクションの任意の場所に1個だけ要素を挿入する。InsertRangeメソッドは、既存のコレクションに格納されている複数の要素をList<T>コレクションの任意の位置から連続して挿入する(次のコード)。

 Insertメソッドで指定できるインデックスは、その時点でのListコレクションの最大インデックスの次の値(例えば4個の要素が格納されているときは「4」)までとなっている。これにより、コレクションの途中に挿入するときも末尾に追加するときもInsertメソッドだけで済ませられるのである。ソート順を維持したまま要素を挿入していくようなコードを書くときに重宝する。

var list1 = new List<string>(strings); // 「strings」変数は前出
DisplayItems(nameof(list1), list1);
// 出力:list1: aaa, bbb, ccc

// Insertメソッド
list1.Insert(1, "2番目");
DisplayItems(nameof(list1), list1);
// 出力:list1: aaa, 2番目, bbb, ccc

list1.Insert(list1.Count, "5番目"); // 最大インデックス+1までOK
DisplayItems(nameof(list1), list1);
// 出力:list1: aaa, 2番目, bbb, ccc, 5番目

// list1.Insert(list1.Count+1, "7番目");
// ↑これはArgumentOutOfRangeExceptionが発生する

// InsertRangeメソッド
list1.InsertRange(3, new[] { "4th", "5th" });
DisplayItems(nameof(list1), list1);
// 出力:list1: aaa, 2番目, bbb, 4th, 5th, ccc, 5番目

Dim list1 = New List(Of String)(strings) ' 「strings」変数は前出
DisplayItems(NameOf(list1), list1)
' 出力:list1: aaa, bbb, ccc

' Insertメソッド
list1.Insert(1, "2番目")
DisplayItems(NameOf(list1), list1)
' 出力:list1: aaa, 2番目, bbb, ccc

list1.Insert(list1.Count, "5番目") '最大インデックス+1までOK
DisplayItems(NameOf(list1), list1)
' 出力:list1: aaa, 2番目, bbb, ccc, 5番目

' list1.Insert(list1.Count + 1, "7番目")
'↑これはArgumentOutOfRangeExceptionが発生する

' InsertRangeメソッド
list1.InsertRange(3, New String() {"4th", "5th"})
DisplayItems(NameOf(list1), list1)
' 出力:list1: aaa, 2番目, bbb, 4th, 5th, ccc, 5番目

Insertメソッド/InsertRangeメソッドを使う例(上:C#、下:VB)

まとめ

 Listに要素を追加するには、初期化時に要素を与える方法、Addメソッド/AddRangeメソッドを使って末尾に追加する方法、そしてInsertメソッド/InsertRangeメソッドを使って任意の場所に挿入するという3通りの方法がある。

 foreach/For Eachループの中で逐一Addメソッドを使って要素を追加しているコードは、AddRangeメソッドを使ってシンプルに書き換えられるかもしれない。検討してみてほしい。また、Insertメソッドでは末尾の要素のその後ろに挿入(すなわち、末尾に追加)できることも覚えておいてもらいたい。

「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。