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

配列の中から指定した範囲の要素を抜き出す

例えば、配列の中でインデックスが2番目の要素から5つの要素を抜き出すというように、指定した範囲の要素を抜き出す方法を紹介します。

ちなみにコレクションでは、GetRangeメソッドを使って簡単に行うことができます。

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

まず、新しい配列を作成して、Array.Copyメソッドでコピーするという単純な方法が考えられます。次の例では、インデックスが2番目の要素から5つの要素を抜き出しています。

VB.NET
コードを隠すコードを選択
'基になる配列 
Dim ary1 As Integer() = New Integer() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

'コピー先の配列を作成する 
Dim ary2 As Integer() = New Integer(4) {}
'指定された範囲をコピーする 
Array.Copy(ary1, 2, ary2, 0, 5)

'ary2は{ 2, 3, 4, 5, 6 }となる
C#
コードを隠すコードを選択
//基になる配列
int[] ary1 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//コピー先の配列を作成する
int[] ary2 = new int[5];
//指定された範囲をコピーする
Array.Copy(ary1, 2, ary2, 0, 5);

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

Buffer.BlockCopyメソッドを使う

Array.Copyメソッドの代わりにBuffer.BlockCopyメソッドを使うと、処理速度が上がります。ただしBuffer.BlockCopyメソッドは.NET Framework 2.0以降でのみ使用でき、さらに、プリミティブ型の配列だけに使用できます。Buffer.BlockCopyメソッドについて詳しくは、「複数の配列をマージする」をご覧ください。

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

VB.NET
コードを隠すコードを選択
'基になる配列 
Dim ary1 As Integer() = New Integer() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

'コピー先の配列を作成する 
Dim ary2 As Integer() = New Integer(4) {}
'型のサイズを取得する(Int32の場合は 4)
Dim typeSize As Integer = System.Runtime.InteropServices.Marshal.SizeOf( _
    ary1.[GetType]().GetElementType())
'指定された範囲をコピーする 
Buffer.BlockCopy(ary1, 2 * typeSize, ary2, 0, ary2.Length * typeSize)
C#
コードを隠すコードを選択
//基になる配列
int[] ary1 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//コピー先の配列を作成する
int[] ary2 = new int[5];
//型のサイズを取得する(Int32の場合は 4)
int typeSize = System.Runtime.InteropServices.Marshal.SizeOf(
    ary1.GetType().GetElementType());
//指定された範囲をコピーする
Buffer.BlockCopy(ary1, 2 * typeSize, ary2, 0, ary2.Length * typeSize);

SkipメソッドとTakeメソッドを使う

.NET Framework 3.5以降でLINQが使えるのであれば、Enumerable.Skipメソッドで指定した要素までスキップして、Enumerable.Takeメソッドで指定した数の要素を取得することができます。

以下にSkipメソッドとTakeメソッドを使った例を示します。

VB.NET
コードを隠すコードを選択
'Imports System.Linq

'基になる配列 
Dim ary1 As Integer() = New Integer() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

'Skipで2つの要素を飛ばして、Takeで5つの要素を取得する 
Dim ary2 As Integer() = ary1.Skip(2).Take(5).ToArray()
C#
コードを隠すコードを選択
//using System.Linq;

//基になる配列
int[] ary1 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//Skipで2つの要素を飛ばして、Takeで5つの要素を取得する
int[] ary2 = ary1.Skip(2).Take(5).ToArray();

SkipとTakeメソッドに似たメソッドに、SkipWhileとTakeWhileメソッドがあります。SkipWhileメソッドは、要素が指定した条件に一致しなくなるまでスキップします。TakeWhileメソッドは、指定した条件に一致している間の要素を返します。

以下の例では、値が2以上の要素までスキップし、値が7未満の要素を取得しています。

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

'基になる配列 
Dim ary1 As Integer() = New Integer() {0, 1, 2, 3, 4, 5, _
6, 7, 8, 9}

'SkipWhileで2未満の要素をスキップし、TakeWhileで7未満の要素を取得する 
Dim ary2 As Integer() = ary1.SkipWhile(Function(i) i < 2). _
    TakeWhile(Function(i) i < 7).ToArray()

'ary2は{ 2, 3, 4, 5, 6 }となる
C#
コードを隠すコードを選択
//using System.Linq;
//がソースファイルの一番上に書かれているものとする

//基になる配列
int[] ary1 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//SkipWhileで2未満の要素をスキップし、TakeWhileで7未満の要素を取得する
int[] ary2 = ary1.SkipWhile(i => i < 2).TakeWhile(i => i < 7).ToArray();

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

コレクションに変換して、GetRangeメソッドを使う

コレクションではGetRangeメソッドを使って指定した範囲の要素を抜き出すことができますので、配列をコレクションに変換してGetRangeメソッドを使うという方法も考えられます。ただしこの方法にはこれといったメリットがありませんので、上記の方法の方が優れているでしょう。

VB.NET
コードを隠すコードを選択
'基になる配列
Dim ary1 As Integer() = New Integer() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

'ary1からコレクションを作成する
'.NET Framework 2.0以降ならば、List(Of Integer)を使った方が良い
Dim al As New System.Collections.ArrayList(ary1)

'2番目から5つの要素を取り出す
Dim rangeList As System.Collections.ArrayList = al.GetRange(2, 5)

'配列に変換する
Dim ary2 As Integer() = _
    DirectCast(rangeList.ToArray(GetType(Integer)), Integer())

'ary2は{ 2, 3, 4, 5, 6 }となる
C#
コードを隠すコードを選択
//基になる配列
int[] ary1 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//ary1からコレクションを作成する
//.NET Framework 2.0以降ならば、List<int>を使った方が良い
System.Collections.ArrayList al = new System.Collections.ArrayList(ary1);

//2番目から5つの要素を取り出す
System.Collections.ArrayList rangeList = al.GetRange(2, 5);

//配列に変換する
int[] ary2 = (int[])rangeList.ToArray(typeof(int));

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

ArraySegment構造体を使う

新しい配列を作るわけではないため、ここで紹介するのが適当ではないかもしれませんが、何かの参考になるかもしれませんので、ArraySegment構造体を紹介しておきます。ArraySegment構造体は、.NET Framework 2.0以降で使用できます。

ArraySegment構造体は、1次元配列のラッパーで、配列内の要素を範囲(開始位置と長さ)を指定して区切るものです。ArraySegment構造体は、配列と開始位置と長さを覚えている程度で、大した機能はありません。

以下にArraySegmentで配列を区切り、区切られた範囲の要素を表示する例を示します。

VB.NET
コードを隠すコードを選択
'基になる配列 
Dim ary1 As Integer() = New Integer() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

'配列を区切る 
Dim arySeg As New ArraySegment(Of Integer)(ary1, 2, 5)

'区切られた範囲の要素を表示する 
For i As Integer = arySeg.Offset To arySeg.Offset + arySeg.Count - 1
    Console.WriteLine("{0}:{1}", i, arySeg.Array(i))
Next

'以下のように表示される 
'2:2 
'3:3 
'4:4 
'5:5 
'6:6 
C#
コードを隠すコードを選択
//基になる配列
int[] ary1 = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//配列を区切る
ArraySegment<int> arySeg = new ArraySegment<int>(ary1, 2, 5);

//区切られた範囲の要素を表示する
for (int i = arySeg.Offset; i < arySeg.Offset + arySeg.Count; i++)
{
    Console.WriteLine("{0}:{1}", i, arySeg.Array[i]);
}

//以下のように表示される
//2:2
//3:3
//4:4
//5:5
//6:6
  • 履歴:
  • 2013/8/25 「Buffer.BlockCopyメソッドを使う」で型のサイズをMarshal.SizeOfで取得するようにした。「コレクションに変換して、GetRangeメソッドを使う」を追加。

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

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。