DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

複数の配列(またはコレクション)をマージ(合併、連結、合体)する

ここでは、複数の配列やコレクションをくっつけて1つにする(マージする)方法を紹介します。例えば、{ 1, 2, 3 }と{ 4, 5, 6 }という配列をマージして、{ 1, 2, 3, 4, 5, 6 }という配列を作るということです。

AddRangeメソッドでコレクションのマージを行う

コレクションの場合は、AddRangeメソッドで簡単に行うことができます。

AddRangeメソッドはマージするコレクションを末尾に追加しますが、InsertRangeメソッドを使えば、挿入する位置を指定することもできます。

以下に示す例では、AddRangeメソッドを使ってArrayListをマージしています。

VB.NET
コードを隠すコードを選択
'マージする2つのコレクション
Dim list1 As New System.Collections.ArrayList(New Integer() {1, 2, 3})
Dim list2 As New System.Collections.ArrayList(New Integer() {4, 5, 6})

'list1の末尾にlist2を追加する
list1.AddRange(list2)

'list1は{ 1, 2, 3, 4, 5, 6 }となる
C#
コードを隠すコードを選択
//マージする2つのコレクション
System.Collections.ArrayList list1 =
    new System.Collections.ArrayList(new int[] { 1, 2, 3 });
System.Collections.ArrayList list2 =
    new System.Collections.ArrayList(new int[] { 4, 5, 6 });

//list1の末尾にlist2を追加する
list1.AddRange(list2);

//list1は{ 1, 2, 3, 4, 5, 6 }となる

次のように配列をコレクションに変換すれば、この方法で配列もマージできます。この例ではListを使用していますが、ArrayListでも同様にできます。

VB.NET
コードを隠すコードを選択
'マージする2つの配列
Dim ary1 As Integer() = New Integer() {1, 2, 3}
Dim ary2 As Integer() = New Integer() {4, 5, 6}

'コレクションを作成する
Dim mergedList As New System.Collections.Generic.List(Of Integer)( _
    ary1.Length + ary2.Length)
'配列をコレクションに追加する
mergedList.AddRange(ary1)
mergedList.AddRange(ary2)

'配列に変換する
Dim mergedArray As Integer() = mergedList.ToArray()

'mergedArrayは{ 1, 2, 3, 4, 5, 6 }となる
C#
コードを隠すコードを選択
//マージする2つの配列
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

//コレクションを作成する
System.Collections.Generic.List<int>
    mergedList = new System.Collections.Generic.List<int>(
        ary1.Length + ary2.Length);
//配列をコレクションに追加する
mergedList.AddRange(ary1);
mergedList.AddRange(ary2);

//配列に変換する
int[] mergedArray = mergedList.ToArray();

//mergedArrayは{ 1, 2, 3, 4, 5, 6 }となる

ただし配列の場合は、以下に紹介する方法の方が優れています。

新しい配列を作成して、Array.Copyメソッドでコピーする

配列の場合は、マージ先として新しい配列を作成しておいて、そこにマージする配列のデータをArray.Copyメソッドでコピーするという方法が考えられます。以下にその例を示します。

VB.NET
コードを隠すコードを選択
'マージする2つの配列 
Dim ary1 As Integer() = New Integer() {1, 2, 3}
Dim ary2 As Integer() = New Integer() {4, 5, 6}

'新しい配列を作る 
Dim mergedArray As Integer() = New Integer(ary1.Length + ary2.Length - 1) {}
'マージする配列のデータをコピーする 
Array.Copy(ary1, mergedArray, ary1.Length)
Array.Copy(ary2, 0, mergedArray, ary1.Length, ary2.Length)

'mergedArrayは{ 1, 2, 3, 4, 5, 6 }となる
C#
コードを隠すコードを選択
//マージする2つの配列
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

//新しい配列を作る
int[] mergedArray = new int[ary1.Length + ary2.Length];
//マージする配列のデータをコピーする
Array.Copy(ary1, mergedArray, ary1.Length);
Array.Copy(ary2, 0, mergedArray, ary1.Length, ary2.Length);

//mergedArrayは{ 1, 2, 3, 4, 5, 6 }となる

Copyメソッドの代わりに、CopyToメソッドを使うこともできます。

VB.NET
コードを隠すコードを選択
Dim ary1 As Integer() = New Integer() {1, 2, 3}
Dim ary2 As Integer() = New Integer() {4, 5, 6}

Dim mergedArray As Integer() = New Integer(ary1.Length + ary2.Length - 1) {}
ary1.CopyTo(mergedArray, 0)
ary2.CopyTo(mergedArray, ary1.Length)
C#
コードを隠すコードを選択
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

int[] mergedArray = new int[ary1.Length + ary2.Length];
ary1.CopyTo(mergedArray, 0);
ary2.CopyTo(mergedArray, ary1.Length);

または、Array.Resizeで連結基の配列を拡大して、連結する配列のデータをArray.Copyでコピーするという方法もあります。Array.Resizeは.NET Framework 2.0以降で使用できます。Array.Resizeは内部で新しい配列を作成してArray.Copyでデータをコピーしているため、結局は一番初めに紹介した例とほぼ同じです。

以下にArray.Resizeを使用した例を示します。

VB.NET
コードを隠すコードを選択
Dim ary1 As Integer() = New Integer() {1, 2, 3}
Dim ary2 As Integer() = New Integer() {4, 5, 6}

Dim aryLen As Integer = ary1.Length
'ary1のサイズを拡大する 
Array.Resize(ary1, ary1.Length + ary2.Length)
'ary2のデータをコピーする 
Array.Copy(ary2, 0, ary1, aryLen, ary2.Length)
C#
コードを隠すコードを選択
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

int aryLen = ary1.Length;
//ary1のサイズを拡大する
Array.Resize(ref ary1, ary1.Length + ary2.Length);
//ary2のデータをコピーする
Array.Copy(ary2, 0, ary1, aryLen, ary2.Length);

Buffer.BlockCopyメソッドを使う

Array.Copyメソッドの代わりにBuffer.BlockCopyメソッドを使うと、処理速度が上がります。ただしBuffer.BlockCopyメソッドは.NET Framework 2.0以降でのみ使用でき、さらに、プリミティブ型の配列だけに使用できます。Bufferクラスが対応しているプリミティブ型は、Boolean、Char、SByte、Byte、Int16、UInt16、Int32、UInt32、Int64、UInt64、IntPtr、UIntPtr、Single、Doubleです。

Buffer.BlockCopyメソッドでは、コピーする位置をインデックスではなくオフセット位置で指定し、オフセット位置とコピーする長さをバイトで指定しなければならない点に注意が必要です。

以下にBuffer.BlockCopyメソッドを使った例を示します。

VB.NET
コードを隠すコードを選択
Dim ary1 As Integer() = New Integer() {1, 2, 3}
Dim ary2 As Integer() = New Integer() {4, 5, 6}

Dim mergedArray As Integer() = New Integer(ary1.Length + (ary2.Length - 1)) {}
'型のサイズを取得する(Int32の場合は 4)
Dim typeSize As Integer = System.Runtime.InteropServices.Marshal.SizeOf( _
    ary1.[GetType]().GetElementType())
'Buffer.BlockCopyでコピーする
Buffer.BlockCopy(ary1, 0, mergedArray, 0, _
                 ary1.Length * typeSize)
Buffer.BlockCopy(ary2, 0, mergedArray, _
                 ary1.Length * typeSize, ary2.Length * typeSize)
C#
コードを隠すコードを選択
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

int[] mergedArray = new int[ary1.Length + ary2.Length];
//型のサイズを取得する(Int32の場合は 4)
int typeSize = System.Runtime.InteropServices.Marshal.SizeOf(
    ary1.GetType().GetElementType());
//Buffer.BlockCopyでコピーする
Buffer.BlockCopy(ary1, 0, mergedArray, 0,
    ary1.Length * typeSize);
Buffer.BlockCopy(ary2, 0, mergedArray, ary1.Length * typeSize,
    ary2.Length * typeSize);

Concatメソッドを使う

LINQが使えるのならば、Concatメソッドで連結することができます。これが使用できるのは.NET Framework 3.5以降で、参照設定に「System.Core.dll」を追加する必要があります。

以下に例を示します。

VB.NET
コードを隠すコードを選択
'Imports System.Linq
'がソースファイルの一番上に書かれているものとする

'マージする配列(コレクションでも可)
Dim ary1 As Integer() = New Integer() {1, 2, 3}
Dim ary2 As Integer() = New Integer() {4, 5, 6}

'Concatで連結して、ToArrayで配列にする 
Dim mergedArray As Integer() = ary1.Concat(ary2).ToArray()
C#
コードを隠すコードを選択
//using System.Linq;
//がソースファイルの一番上に書かれているものとする

//マージする配列(コレクションでも可)
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

//Concatで連結して、ToArrayで配列にする
int[] mergedArray = ary1.Concat(ary2).ToArray();

IEnumerable(T)を返すメソッドを作る

yieldを利用してIEnumerable(T)を返すメソッドを作成する方法を紹介します。この方法は.NET Framework 2.0以降のC#で使えます。

ここでは2つのInteger型配列を連結する方法を例にします。まず次のようなメソッドを作成します。yield returnで配列の要素を順番に返しているだけです。

C#
コードを隠すコードを選択
//2つの整数型配列を連結するメソッド
IEnumerable<int> Merge(int[] ary1, int[] ary2)
{
    foreach (int i in ary1)
        yield return i;
    foreach (int i in ary2)
        yield return i;
}

このメソッドを利用して2つの配列を連結するには、次のようにします。

C#
コードを隠すコードを選択
//連結する配列
int[] ary1 = new int[] { 1, 2, 3 };
int[] ary2 = new int[] { 4, 5, 6 };

//連結する
int[] mergedArray = Merge(ary1, ary2).ToArray();

Unionメソッドを使う

Unionメソッドの説明は「要素が重複しないようにして、複数の配列(またはコレクション)をマージ(和集合を作成)する」に移動しました。

  • 履歴:
  • 2009/9/9 IEnumerable(T)を返すメソッドを作る方法を追加。
  • 2013/7/28 AddRangeメソッドの詳しい説明を追加。Buffer.BlockCopyのサンプルで、型のサイズを定数ではなく、Marshal.SizeOfで取得するようにした。
  • 2013/8/18 InsertRangeメソッドの説明を追加。Unionメソッドの説明を別のページに移動。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • 「???を参照に追加します」の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。