.NETにおける配列はArrayクラスを基本クラスとしていて、すべての配列は暗黙的にこのクラスを継承しています。 配列の基本的な機能はすべてこのクラスにより提供されます。 また、Arrayクラスには様々なメソッドが用意されていて、それらを使うことで言語の構文では用意されていない配列操作などを行うことが出来ます。
ここで解説する配列の操作と、対応するArrayクラスのメソッドを表にまとめます。 個々のメソッドの詳細については順に解説していきます。
配列操作 | 対応するArrayクラスのメソッド | 解説へのリンク | |
---|---|---|---|
配列をクリアする | Clear | 解説へ | |
配列を同じ値で埋める | Fill | 解説へ | |
配列のサイズ(長さ)を変更する | Resize | 解説へ | |
配列を読み取り専用にする | AsReadOnly | 解説へ | |
コピー (複製・複写) |
配列の複製を作成する | Clone | 解説へ |
配列の一部・全部を他の配列に複写する | Copy, CopyTo | 解説へ | |
並べ替え | 配列内の要素を逆順に(リバース)する | Reverse | 解説へ |
配列内の要素をソートする | Sort | 解説へ | |
要素の検索 | 配列内にある要素を検索してインデックスを取得する | IndexOf, LastIndexOf, BinarySearch | 解説へ |
述語(Predicate)を使って配列内の要素を検索する | Find, FindLast, FindAll, FindIndex, FindLastIndex,Exists, TrueForAll | 解説へ | |
全要素に対する操作 | 配列内の全要素を他の型・値に変換する | ConvertAll | 解説へ |
配列内の全要素を列挙する | ForEach | 解説へ | |
構文によらない配列の操作を行う | CreateInstance, GetValue, SetValue | 解説へ | |
配列操作 | 対応するArrayクラスのメソッド | 解説へのリンク |
クリア (Clear)
Array.Clearメソッドを使うことで、配列内の要素をクリアして初期化することが出来ます。 このメソッドを使って配列内をクリアすると、配列内の各要素にデフォルト値が設定されます。 デフォルト値は型によって異なりますが、数値などの型では0や0に相当する値、文字列型を含む参照型ではヌル参照(null/Nothing)となります。
どのような値で初期化されるか、型とデフォルト値の詳細については型の種類・サイズ・精度・値域 §.型のデフォルト値を参照してください。
また、このメソッドは引数としてクリアを開始するインデックスと長さをとるため、配列の一部分だけをクリアすることも出来ます。
using System;
class Sample {
static void Main()
{
int[] arr = {0, 1, 2, 3, 4};
// インデックスが3の要素から2個分をクリア
Array.Clear(arr, 3, 2);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// 配列の全要素をクリア
Array.Clear(arr, 0, arr.Length);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr() As Integer = {0, 1, 2, 3, 4}
' インデックスが3の要素から2個分をクリア
Array.Clear(arr, 3, 2)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' 配列の全要素をクリア
Array.Clear(arr, 0, arr.Length)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 2, 0, 0, 0, 0, 0, 0, 0,
なお、Array.Clearメソッドは配列内の要素をクリアするためのもので、配列の長さを0にするものではありません。 配列の長さを0にするにはResizeメソッドを使います。
Array.Clearメソッドは、0を指定したmemset
、あるいはZeroMemory
マクロのような動作となります。 Array.Clearメソッドでは配列を0以外の値にクリアする事はできません。 配列全体を0以外の同一の値にセットする、memset
やFillMemory
マクロのような動作を求める場合は、Array.Fillメソッドを使用するか、for文等を使って個別に実装する必要があります。
ジャグ配列・多次元配列のクリア
Array.Clearメソッドでジャグ配列をクリアする場合は、ジャグ配列の1段目に格納されている配列がクリアされヌル参照(null/Nothing)の状態になります。 ジャグ配列に格納されている各配列はクリアされません。
using System;
class Sample {
static void Main()
{
// ジャグ配列に格納する配列
int[] arr1 = {0, 1, 2, 3, 4};
int[] arr2 = {5, 6, 7};
// 2段のジャグ配列
int[][] jagged = {
arr1,
arr2,
};
// ジャグ配列をクリア
Array.Clear(jagged, 0, jagged.Length);
Console.WriteLine("jagged[0] == null : {0}", jagged[0] == null);
Console.WriteLine("jagged[1] == null : {0}", jagged[1] == null);
foreach (var elem in arr1) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
jagged[0] == null : True jagged[1] == null : True 0, 1, 2, 3, 4,
Imports System
Class Sample
Shared Sub Main()
' ジャグ配列に格納する配列
Dim arr1() As Integer = {0, 1, 2, 3, 4}
Dim arr2() As Integer = {5, 6, 7}
' 2段のジャグ配列
Dim jagged()() As Integer = { _
arr1, _
arr2 _
}
' ジャグ配列をクリア
Array.Clear(jagged, 0, jagged.Length)
Console.WriteLine("jagged(0) Is Nothing : {0}", jagged(0) Is Nothing)
Console.WriteLine("jagged(1) Is Nothing : {0}", jagged(1) Is Nothing)
For Each elem As Integer In arr1
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
jagged(0) Is Nothing : True jagged(1) Is Nothing : True 0, 1, 2, 3, 4,
一方、Array.Clearメソッドでは多次元配列をクリアすることも出来ますが、その際多次元配列は1次元配列の様に扱われます。 Array.Clearメソッドの引数に指定するインデックスは、多次元配列を1次元配列に展開したときの位置として解釈されるという点に注意が必要です。
using System;
class Sample {
static void Main()
{
int[,] matrix = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
};
// 2次元配列全体の3番目の要素から4個分をクリア
Array.Clear(matrix, 3, 4);
for (var d1 = 0; d1 < matrix.GetLength(0); d1++) {
for (var d2 = 0; d2 < matrix.GetLength(1); d2++) {
Console.Write("{0}, ", matrix[d1, d2]);
}
Console.WriteLine();
}
}
}
Imports System
Class Sample
Shared Sub Main()
Dim matrix As Integer(,) = { _
{0, 1, 2, 3}, _
{4, 5, 6, 7}, _
{8, 9, 10, 11} _
}
' 2次元配列の一部分をクリア
Array.Clear(matrix, 3, 4)
For d1 As Integer = 0 To matrix.GetLength(0) - 1
For d2 As Integer = 0 To matrix.GetLength(1) - 1
Console.Write("{0}, ", matrix(d1, d2))
Next
Console.WriteLine()
Next
End Sub
End Class
0, 1, 2, 0, 0, 0, 0, 7, 8, 9, 10, 11,
同じ値で埋める (Fill)
Array.Fillメソッドを用いることで、配列内のすべての要素を同じ値に設定することができます。 Array.Clearメソッドが配列内すべてを0に設定するのに対して、Fillメソッドでは任意の値に設定することができます。 また、Clearメソッドと同様に、配列内の範囲を指定してその要素だけを同じ値に設定することもできます。 Array.Fillメソッドは.NET Standard 2.1/.NET Core 2.0以降で使用できます。
using System;
class Sample {
static void Main()
{
int[] arr = {0, 1, 2, 3, 4};
// 配列の全要素を-1に設定する
Array.Fill(arr, -1);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// インデックスが1の要素から3個分を9に設定する
Array.Fill(arr, 9, 1, 3);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr() As Integer = {0, 1, 2, 3, 4}
' 配列の全要素を-1に設定する
Array.Fill(arr, -1)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' インデックスが1の要素から3個分を9に設定する
Array.Fill(arr, 9, 1, 3)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
-1, -1, -1, -1, -1, -1, 9, 9, 9, -1,
Array.Clearメソッドは二次元以上の配列にも対応していますが、Array.Fillメソッドは一次元配列のみに対応しています。
リサイズ (Resize)
Array.Resizeメソッドを使うことで、配列の長さを変更することが出来ます。 このメソッドによってリサイズされるというのは見た目の動作で、実際には、変更後のサイズの配列が新たに作成され、その配列に元の配列の内容をコピーしたものが引数で指定した配列と置き換えられるという動作になります。 このメソッドでは、配列の長さを元の長さよりも短くすることも長くすることも可能です。 Array.Resizeメソッドで配列を拡張した場合、拡張した部分にはデフォルト値がセットされます。
using System;
class Sample {
static void Main()
{
int[] arr = {0, 1, 2, 3, 4};
// 配列の長さを長くする
Array.Resize(ref arr, 7);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// 配列の長さを短くする
Array.Resize(ref arr, 3);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr() As Integer = {0, 1, 2, 3, 4}
' 配列の長さを長くする
Array.Resize(arr, 7)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' 配列の長さを短くする
Array.Resize(arr, 3)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 2, 3, 4, 0, 0, 0, 1, 2,
ジャグ配列をリサイズすることも出来ますが、その場合は1段目の長さのみが変更できます。 2段目以降の長さを変更する場合は、格納されている配列に対して個別にArray.Resizeメソッドを呼び出してリサイズする必要があります。
なお、Array.Resizeメソッドでは多次元配列のサイズを変更することは出来ません。
複製・複写
複製 (Clone)
Array.Cloneメソッドを使うことで、配列を複製して同じ内容を持った配列を生成することが出来ます。 Array.Cloneメソッドの戻り値はobjectであるため、必要に応じて複製元と同じ型にキャストして使います。
using System;
class Sample {
static void Main()
{
int[] arr1 = {0, 1, 2, 3, 4};
int[] arr2;
// arr1を複製してarr2に代入
arr2 = (int[])arr1.Clone();
// 複製元と複製後の配列の要素を変更
arr1[2] = 0;
arr2[2] = 5;
// それぞれの配列の内容を表示
foreach (var elem in arr1) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr1() As Integer = {0, 1, 2, 3, 4}
Dim arr2() As Integer
' arr1を複製してarr2に代入
arr2 = DirectCast(arr1.Clone(), Integer())
' 複製元と複製後の配列の要素を変更
arr1(2) = 0
arr2(2) = 5
' それぞれの配列の内容を表示
For Each elem As Integer In arr1
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 0, 3, 4, 0, 1, 5, 3, 4,
このメソッドでは簡易コピーが行われるため、参照型を要素に持つ配列では参照のみがコピーされます。 そのため、ジャグ配列の複製を行うと、複製元と複製後のジャグ配列内における2段目以降はどちらも同一のインスタンスを参照する事になります。
Cloneメソッドと簡易コピーについてより詳しくはオブジェクトの複製で解説しています。
多次元配列を複製する場合の結果は1次元配列を複製する場合と同様で、長さ・次元数・格納される要素が同一の多次元配列が生成されます。
using System;
class Sample {
static void Main()
{
int[,] matrix1 = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
};
int[,] matrix2;
// matrix1を複製してmatrix2に代入
matrix2 = (int[,])matrix1.Clone();
// 複製元と複製後の配列の要素を変更
matrix1[0, 0] = 99;
matrix2[0, 0] = -1;
// それぞれの配列の内容を表示
for (var d1 = 0; d1 < matrix1.GetLength(0); d1++) {
for (var d2 = 0; d2 < matrix1.GetLength(1); d2++) {
Console.Write("{0}, ", matrix1[d1, d2]);
}
Console.WriteLine();
}
Console.WriteLine();
for (var d1 = 0; d1 < matrix2.GetLength(0); d1++) {
for (var d2 = 0; d2 < matrix2.GetLength(1); d2++) {
Console.Write("{0}, ", matrix2[d1, d2]);
}
Console.WriteLine();
}
}
}
Imports System
Class Sample
Shared Sub Main()
Dim matrix1 As Integer(,) = { _
{0, 1, 2, 3}, _
{4, 5, 6, 7}, _
{8, 9, 10, 11} _
}
Dim matrix2 As Integer(,)
' matrix1を複製してmatrix2に代入
matrix2 = DirectCast(matrix1.Clone(), Integer(,))
' 複製元と複製後の配列の要素を変更
matrix1(0, 0) = 99
matrix2(0, 0) = -1
' それぞれの配列の内容を表示
For d1 As Integer = 0 To matrix1.GetLength(0) - 1
For d2 As Integer = 0 To matrix1.GetLength(1) - 1
Console.Write("{0}, ", matrix1(d1, d2))
Next
Console.WriteLine()
Next
Console.WriteLine()
For d1 As Integer = 0 To matrix2.GetLength(0) - 1
For d2 As Integer = 0 To matrix2.GetLength(1) - 1
Console.Write("{0}, ", matrix2(d1, d2))
Next
Console.WriteLine()
Next
End Sub
End Class
99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
複写 (Copy, CopyTo)
Copyメソッド
Array.Copyメソッドは、Array.Cloneメソッドとは異なり配列の内容を既に存在する別の配列に複写します。 引数で複写する長さ(要素数)を指定できるので、配列の途中までをコピーするということも出来ます。 当然、複写先の配列は複写元と同じがそれ以上の長さを持っている必要があります。 Array.Copyメソッドも、Array.Cloneメソッドと同様要素の簡易コピーを行います。
using System;
class Sample {
static void Main()
{
// 複写元の配列
int[] arr1 = {0, 1, 2, 3, 4};
// 初期化されていない配列を確保
var arr2 = new int[5];
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// arr1の要素の先頭から3個分をarr2に複写
Array.Copy(arr1, arr2, 3);
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// arr1の全要素をarr2に複写
Array.Copy(arr1, arr2, arr1.Length);
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の配列
Dim arr1() As Integer = {0, 1, 2, 3, 4}
' 初期化されていない配列を確保
Dim arr2(4) As Integer
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' arr1の要素の先頭から3個分をarr2に複写
Array.Copy(arr1, arr2, 3)
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' arr1の全要素をarr2に複写
Array.Copy(arr1, arr2, arr1.Length)
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1, 2, 3, 4,
配列のバイト表現を別の配列へ転写する目的(バイト列としてのコピー)にはBuffer.BlockCopyメソッドを使うことができます。 詳しくはバイト列操作 §.BlockCopyメソッドを参照してください。
Array.Copyメソッドは、複写元と複写先に同じ配列を指定することができます。 またArray.Copyメソッドの動作は、memcpy
ではなくmemmove
相当の動作になります。 詳しくは§.同一配列への複写を参照してください。
Array.Copyメソッドでは、複写する長さだけでなく、複写元と複写先の始点となるインデックスを指定して複写することも出来ます。 Arrayクラスには部分配列を抜き出すArray.Sliceのようなメソッドは用意されていませんが、次の例のように配列の生成と部分配列の複写を行うことで、Array.Slice相当の処理を行うことが出来ます。
using System;
class Sample {
static void Main()
{
// 複写元の配列
int[] arr1 = {0, 1, 2, 3, 4};
// 初期化されていない配列を確保
var arr2 = new int[3];
// arr1の部分配列(インデックス2から3個分)をarr2のインデックス0以降に複写
Array.Copy(arr1, 2, arr2, 0, 3);
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の配列
Dim arr1() As Integer = {0, 1, 2, 3, 4}
' 初期化されていない配列を確保
Dim arr2(2) As Integer
' arr1の部分配列(インデックス2から3個分)をarr2のインデックス0以降に複写
Array.Copy(arr1, 2, arr2, 0, 3)
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
2, 3, 4,
この方法では部分配列を取得するために新たな配列インスタンスの作成と複写を行うことになりますが、ArraySegmentを使えばインスタンス生成や複写を行わずに部分配列のビューを取得・作成することもできます。
C# 8.0以降、.NET Core 3.0以降では、インデックス/範囲構文n..m
を使うことで部分配列の抽出を行うことができるようになっています。
using System;
class Sample {
static void Main()
{
// 複写元の配列
int[] arr1 = {0, 1, 2, 3, 4};
// arr1の部分配列(インデックス2から5より前まで、2以上5未満の3個分)をarr2として抽出
var arr2 = arr1[2..5];
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
2, 3, 4,
部分配列の取得と同様に、配列の連結を行うArray.Concatのようなメソッドも用意されていませんが、次の例のように連結後の配列の生成と連結元の配列の複写を行うことで、Array.Concat相当の処理を行うことが出来ます。
using System;
class Sample {
static void Main()
{
// 連結元の配列
int[] arr1 = {0, 1, 2, 3, 4};
int[] arr2 = {5, 6, 7};
// 連結後の配列となる、初期化されていない配列を確保
var arr3 = new int[arr1.Length + arr2.Length];
// arr1の内容をarr3の先頭に複写
Array.Copy(arr1, 0, arr3, 0, arr1.Length);
// arr2の内容をarr3の続きに複写
Array.Copy(arr2, 0, arr3, arr1.Length, arr2.Length);
foreach (var elem in arr3) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
' 連結元の配列
Dim arr1() As Integer = {0, 1, 2, 3, 4}
Dim arr2() As Integer = {5, 6, 7}
' 連結後の配列となる、初期化されていない配列を確保
Dim arr3(arr1.Length + arr2.Length - 1) As Integer
' arr1の内容をarr3の先頭に複写
Array.Copy(arr1, 0, arr3, 0, arr1.Length)
' arr2の内容をarr3の続きに複写
Array.Copy(arr2, 0, arr3, arr1.Length, arr2.Length)
For Each elem As Integer In arr3
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 2, 3, 4, 5, 6, 7,
多次元配列の複写
多次元配列もArray.Copyメソッドで複写できます。 多次元配列を複写する場合は、次元数が同じで複写先の長さが十分であれば、複写元と複写先で次元毎の長さが異なっていても複写することが出来ます。
using System;
class Sample {
static void Main()
{
// 複写元の2次元配列(長さが4×3)
int[,] matrix1 = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
};
// 複写先の2次元配列(長さが3×5)を確保
var matrix2 = new int[5, 3];
// 配列を複写
Array.Copy(matrix1, matrix2, matrix1.Length);
// それぞれの配列の内容を表示
Print(matrix1);
Console.WriteLine();
Print(matrix2);
}
static void Print(int[,] matrix)
{
for (var d1 = 0; d1 < matrix.GetLength(0); d1++) {
for (int d2 = 0; d2 < matrix.GetLength(1); d2++) {
Console.Write("{0}, ", matrix[d1, d2]);
}
Console.WriteLine();
}
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の2次元配列(長さが4×3)
Dim matrix1 As Integer(,) = { _
{0, 1, 2, 3}, _
{4, 5, 6, 7}, _
{8, 9, 10, 11} _
}
' 複写先の2次元配列(長さが3×5)を確保
Dim matrix2(4, 2) As Integer
' 配列を複写
Array.Copy(matrix1, matrix2, matrix1.Length)
' それぞれの配列の内容を表示
Print(matrix1)
Console.WriteLine()
Print(matrix2)
End Sub
Shared Sub Print(ByVal matrix As Integer(,))
For d1 As Integer = 0 To matrix.GetLength(0) - 1
For d2 As Integer = 0 To matrix.GetLength(1) - 1
Console.Write("{0}, ", matrix(d1, d2))
Next
Console.WriteLine()
Next
End Sub
End Class
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0,
Copyメソッドでは次元数が異なる配列への複写が出来ないので、そういった場合はfor文・foreach文を使って要素を一つずつコピーする必要があります。
using System;
class Sample {
static void Main()
{
// 複写元の1次元配列(長さが12)
int[] matrix1 = {
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
};
// 複写先の2次元配列(長さが4×3)を確保
var matrix2 = new int[3, 4];
// 配列を複写
for (var i = 0; i < matrix1.Length; i++) {
int d1 = i / matrix2.GetLength(1);
int d2 = i % matrix2.GetLength(1);
matrix2[d1, d2] = matrix1[i];
}
// 結果を表示
for (var d1 = 0; d1 < matrix2.GetLength(0); d1++) {
for (var d2 = 0; d2 < matrix2.GetLength(1); d2++) {
Console.Write("{0}, ", matrix2[d1, d2]);
}
Console.WriteLine();
}
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の1次元配列(長さが12)
Dim matrix1 As Integer() = { _
0, 1, 2, 3, _
4, 5, 6, 7, _
8, 9, 10, 11 _
}
' 複写先の2次元配列(長さが4×3)を確保
Dim matrix2(2, 3) As Integer
' 配列を複写
For i As Integer = 0 To matrix1.Length - 1
Dim d1 As Integer = i \ matrix2.GetLength(1)
Dim d2 As Integer = i Mod matrix2.GetLength(1)
matrix2(d1, d2) = matrix1(i)
Next
' 結果を表示
For d1 As Integer = 0 To matrix2.GetLength(0) - 1
For d2 As Integer = 0 To matrix2.GetLength(1) - 1
Console.Write("{0}, ", matrix2(d1, d2))
Next
Console.WriteLine()
Next
End Sub
End Class
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
同一配列への複写
Array.Copyメソッドでは、複写先を複写元と同一の配列にすることが出来ます。 この際、複写先と複写元の一部が重なっていても問題なく複写されます。
using System;
class Sample {
static void Main()
{
// 複写元の配列
int[] arr = {0, 1, 2, 3, 4};
// arrの部分配列(インデックス0から3個分)をarr自身のインデックス2以降にコピー
Array.Copy(arr, 0, arr, 2, 3);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の配列
Dim arr() As Integer = {0, 1, 2, 3, 4}
' arrの部分配列(インデックス0から3個分)をarr自身のインデックス2以降にコピー
Array.Copy(arr, 0, arr, 2, 3)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 0, 1, 2,
このように、Array.Copyメソッドはmemcpy
ではなくmemmove
に相当する動作となります。
異なる型の配列への複写
複写元と複写先の型に互換性がある場合、もしくは型が複写元と複写先がどちらもプリミティブ型で複写が拡大変換となる場合に限り、異なる型の配列へ複写することが出来ます。
例えば、次の例のように、ともにプリミティブ型であり拡大変換となるint型配列からlong型配列への複写は正常に行われます。
using System;
class Sample {
static void Main()
{
// 複写元の配列 (int型)
int[] source = {0, 1, 2, 3, 4};
// 複写先の配列 (long型)
var dest = new long[5];
// int型の配列をlong型の配列に複写
Array.Copy(source, dest, 5);
foreach (var elem in dest) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の配列 (Integer型)
Dim source() As Integer = {0, 1, 2, 3, 4}
' 複写先の配列 (Long型)
Dim dest(4) As Long
' Integer型の配列をLong型の配列に複写
Array.Copy(source, dest, 5)
For Each elem As Long In dest
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 2, 3, 4,
先に挙げた以外の場合、すなわち型に互換性がない、どちらもプリミティブ型でない、型の変換が縮小変換となるのいずれかの場合では、例外ArrayTypeMismatchExceptionがスローされます。
例えば、上記の例を逆にしてlong型配列からint型配列に複写しようとする場合、どちらの型もプリミティブではあってもlongからintへは縮小変換となるため、ArrayTypeMismatchExceptionがスローされます。
型の分類、プリミティブ型については型の種類・サイズ・精度・値域、拡大変換・縮小変換については基本型の型変換で詳しく解説しています。
Buffer.BlockCopyメソッドでは、プリミティブ型であれば縮小変換となるような配列へのコピーも行うことができます。 詳しくはバイト列操作 §.BlockCopyメソッドを参照してください。
CopyToメソッド
Array.Copyメソッドの他にも、Array.CopyToメソッドを使って複写することも出来ます。 このメソッドはArray.Copyとは異なりインスタンスメソッドです。 また、引数に指定するのは複写先の始点となるインデックスで、常に複写元の配列全体が複写されます。
using System;
class Sample {
static void Main()
{
// 複写元の配列
int[] arr1 = {0, 1, 2, 3, 4};
// 初期化されていない配列を確保
var arr2 = new int[7];
// arr1の全要素をarr2のインデックス1以降に複写
arr1.CopyTo(arr2, 1);
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
' 複写元の配列
Dim arr1() As Integer = {0, 1, 2, 3, 4}
' 初期化されていない配列を確保
Dim arr2(6) As Integer
' arr1の全要素をarr2のインデックス1以降に複写
arr1.CopyTo(arr2, 1)
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 0, 1, 2, 3, 4, 0,
CopyToはインスタンスメソッドであるという点以外は、Copyメソッドと同様で、その動作もCopyメソッドと同じです。
配列の作成・要素の取得と設定 (CreateInstance, GetValue, SetValue)
言語で用意されている構文を使う他に、Array.CreateInstanceメソッドを使うことで任意の型・任意の長さの配列を作成することが出来ます。 このメソッドの戻り値はArrayクラスとなるので、必要に応じて適切な配列型にキャストしてから使います。 このメソッドで作成した配列は、通常の構文で作成した配列と何ら変わりありません。
また、Array.GetValueメソッドおよびArray.SetValueメソッドを使うことで、配列の各要素にアクセスするための構文(インデクサ)を使用せずに配列に値を取得・設定することが出来ます。 このメソッドは配列の型・次元がどのようなものでも値を取得・設定することが出来ます。
using System;
class Sample {
static void Main()
{
// 型がintで長さが5の配列を作成
var arr1 = (int[])Array.CreateInstance(typeof(int), 5);
arr1[0] = 0;
arr1[1] = 1;
arr1[2] = 2;
arr1[3] = 3;
arr1[4] = 4;
foreach (var elem in arr1) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
Console.WriteLine();
// 型がstringで長さが3の配列を作成
var arr2 = Array.CreateInstance(typeof(string), 3);
// SetValueメソッドを使って値を設定
arr2.SetValue("Alice", 0); // 配列のインデックス0に文字列"Alice"を設定
arr2.SetValue("Bob", 1); // 配列のインデックス1に文字列"Bob"を設定
arr2.SetValue("Charlie", 2); // 配列のインデックス2に文字列"Charlie"を設定
// GetValueメソッドを使って値を取得
for (var i = 0; i < arr2.Length; i++) {
Console.WriteLine("arr2[{0}] = {1}", i, arr2.GetValue(i));
}
}
}
Imports System
Class Sample
Shared Sub Main()
' 型がIntegerで長さが5の配列を作成
Dim arr1() As Integer = DirectCast(Array.CreateInstance(GetType(Integer), 5), Integer())
arr1(0) = 0
arr1(1) = 1
arr1(2) = 2
arr1(3) = 3
arr1(4) = 4
For Each elem As Integer In arr1
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
Console.WriteLine()
' 型がStringで長さが3の配列を作成
Dim arr2 As Array = Array.CreateInstance(GetType(String), 3)
' SetValueメソッドを使って値を設定
arr2.SetValue("Alice", 0) ' 配列のインデックス0に文字列"Alice"を設定
arr2.SetValue("Bob", 1) ' 配列のインデックス1に文字列"Bob"を設定
arr2.SetValue("Charlie", 2) ' 配列のインデックス2に文字列"Charlie"を設定
' GetValueメソッドを使って値を取得
For i As Integer = 0 To arr2.Length - 1
Console.WriteLine("arr2({0}) = {1}", i, arr2.GetValue(i))
Next
End Sub
End Class
0, 1, 2, 3, 4, arr2(0) = Alice arr2(1) = Bob arr2(2) = Charlie
GetValueメソッド・SetValueメソッドは要素単位で値の取得・設定を行います。 バイト単位で配列内の値を取得・設定したい場合はBuffer.GetByteメソッド・Buffer.SetByteメソッドを使うことができます。 詳しくはバイト列操作 §.バイト単位での値の設定・取得 (GetByteメソッド・SetByteメソッド)を参照してください。
Array.CreateInstance, Array.GetValue, Array.SetValueの各メソッドは、多次元配列でも使うことが出来ます。 次の例では、Array.CreateInstanceメソッドで指定された型の行列を表す2次元配列を作成し、Array.SetValueメソッドで値を設定して単位行列を構成しています。
using System;
class Sample {
// 任意の長さ・型の単位行列を生成する
static Array CreateIdentityMatrix(int length, Type typeOfMatrix)
{
// 指定された型でlength×length行列となる2次元配列を作成
var matrix = Array.CreateInstance(typeOfMatrix, length, length);
// 行列の対角成分に1、それ以外を0に設定する
for (var i = 0; i < length; i++) {
for (var j = 0; j < length; j++) {
if (i == j)
matrix.SetValue(1, i, j);
else
matrix.SetValue(0, i, j);
}
}
return matrix;
}
// 行列を表示する
static void PrintMatrix(Array matrix)
{
for (var i = 0; i < matrix.GetLength(1); i++) {
Console.Write("(");
for (var j = 0; j < matrix.GetLength(0); j++) {
Console.Write("{0,-5} ", matrix.GetValue(i, j));
}
Console.WriteLine(")");
}
}
static void Main()
{
// int型で2行2列の単位行列を作成
var matrix1 = (int[,])CreateIdentityMatrix(2, typeof(int));
PrintMatrix(matrix1);
Console.WriteLine();
// float型で4行4列の単位行列を作成
var matrix2 = (float[,])CreateIdentityMatrix(4, typeof(float));
// 行列内の各要素をスケーリング
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
matrix2[i, j] *= 0.25f;
}
}
PrintMatrix(matrix2);
Console.WriteLine();
}
}
Imports System
Class Sample
' 任意の長さ・型の単位行列を生成する
Shared Function CreateIdentityMatrix(ByVal length As Integer, ByVal typeOfMatrix As Type) As Array
' 指定された型でlength×length行列となる2次元配列を作成
Dim matrix As Array = Array.CreateInstance(typeOfMatrix, length, length)
' 行列の対角成分に1、それ以外を0に設定する
For i As Integer = 0 To length - 1
For j As Integer = 0 To length - 1
If i = j Then
matrix.SetValue(1, i, j)
Else
matrix.SetValue(0, i, j)
End If
Next
Next
Return matrix
End Function
' 行列を表示する
Shared Sub PrintMatrix(ByVal matrix As Array)
For i As Integer = 0 To matrix.GetLength(1) - 1
Console.Write("(")
For j As Integer = 0 To matrix.GetLength(0) - 1
Console.Write("{0,-5} ", matrix.GetValue(i, j))
Next
Console.WriteLine(")")
Next
End Sub
Shared Sub Main()
' Integer型で2行2列の単位行列を作成
Dim matrix1 As Integer(,) = DirectCast(CreateIdentityMatrix(2, GetType(Integer)), Integer(,))
PrintMatrix(matrix1)
Console.WriteLine()
' Single型で4行4列の単位行列を作成
Dim matrix2 As Single(,) = DirectCast(CreateIdentityMatrix(4, GetType(Single)), Single(,))
' 行列内の各要素をスケーリング
For i As Integer = 0 To 3
For j As Integer = 0 To 3
matrix2(i, j) *= 0.25F
Next
Next
PrintMatrix(matrix2)
Console.WriteLine()
End Sub
End Class
(1 0 ) (0 1 ) (0.25 0 0 0 ) (0 0.25 0 0 ) (0 0 0.25 0 ) (0 0 0 0.25 )
読み取り専用化 (AsReadOnly)
Array.AsReadOnlyメソッドを使うことで、配列を元に読み取り専用のコレクションを作成することが出来ます。
using System;
using System.Collections.ObjectModel;
class Sample {
static void Main()
{
int[] source = {0, 1, 2, 3, 4};
// 配列を読み取り専用にする
var arr = Array.AsReadOnly(source);
// 要素を列挙
for (var i = 0; i < arr.Count; i++) {
Console.Write("{0}, ", arr[i]);
}
Console.WriteLine();
// 読み取り専用のため、要素の設定は出来ない
// sample.cs(20,5): error CS0200: プロパティまたはインデクサー 'System.Collections.ObjectModel.ReadOnlyCollection<int>.this[int]' は読み取り専用なので、割り当てることはできません。
//arr[1] = 5;
// IListインターフェイスを経由して、無理やり要素を変更しようとする
(arr as System.Collections.IList)[0] = 5;
}
}
Imports System
Imports System.Collections.ObjectModel
Class Sample
Shared Sub Main()
Dim source() As Integer = {0, 1, 2, 3, 4}
' 配列を読み取り専用にする
Dim arr As ReadOnlyCollection(Of Integer) = Array.AsReadOnly(source)
' 要素を列挙
For i As Integer = 0 To arr.Count - 1
Console.Write("{0}, ", arr(i))
Next
Console.WriteLine()
' 読み取り専用のため、要素の設定は出来ない
' E:\sample.vb(19) : error BC30526: プロパティ 'Item' は 'ReadOnly' です。
'arr(1) = 5
' IListインターフェイスを経由して、無理やり要素を変更しようとする
DirectCast(arr, System.Collections.IList)(0) = 5
End Sub
End Class
0, 1, 2, 3, 4, ハンドルされていない例外: System.NotSupportedException: コレクションは読み取り専用です。 場所 System.Collections.ObjectModel.ReadOnlyCollection`1.System.Collections.IList.set_Item(Int32 index, Object value) 場所 Sample.Main()
Array.AsReadOnlyメソッドの戻り値はReadOnlyCollectionで、これは読み取り専用にした配列のように振る舞います。 例えば、インデックスを指定して要素に値を設定しようとするとコンパイルエラーとなり、IList等のインターフェイスにキャストして無理やり変更しようとしてもNotSupportedExceptionがスローされます。
それ以外の操作、例えば要素の参照や値の列挙は、通常の配列と同様に振る舞います。 なお、通常の配列と異なりReadOnlyCollectionにはLengthプロパティが無いので、代わりにCountプロパティを参照します。
ReadOnlyCollectionについてより詳しくは汎用ジェネリックコレクション(1) Collection/ReadOnlyCollection §.ReadOnlyCollectionを参照してください。
Array.AsReadOnlyメソッドは読み取り専用のラッパーを作成するだけであり、元となった配列が読み取り専用となるのではないため、元の配列に対しては依然として値を設定することが可能です。 元の配列を変更すると、作成したReadOnlyCollectionにもその変更が反映されます。
using System;
using System.Collections.ObjectModel;
class Sample {
static void Main()
{
int[] source = {0, 1, 2, 3, 4};
// 配列を読み取り専用にする
var arr = Array.AsReadOnly(source);
// 元になった配列に変更を加える
source[0] = 5;
// 読み取り専用コレクションの要素を列挙
for (var i = 0; i < arr.Count; i++) {
Console.Write("{0}, ", arr[i]);
}
Console.WriteLine();
}
}
Imports System
Imports System.Collections.ObjectModel
Class Sample
Shared Sub Main()
Dim source() As Integer = {0, 1, 2, 3, 4}
' 配列を読み取り専用にする
Dim arr As ReadOnlyCollection(Of Integer) = Array.AsReadOnly(source)
' 元になった配列に変更を加える
source(0) = 5
' 読み取り専用コレクションの要素を列挙
For i As Integer = 0 To arr.Count - 1
Console.Write("{0}, ", arr(i))
Next
Console.WriteLine()
End Sub
End Class
5, 1, 2, 3, 4,
要素の並べ替え
リバース (Reverse)
Array.Reverseメソッドを使うことで配列内の全要素の並びを逆順に(リバース)することが出来ます。 逆順にする最初のインデックスと要素数を指定することで、配列内の一部分だけを逆順にすることも出来ます。
using System;
class Sample {
static void Main()
{
int[] arr = {0, 1, 2, 3, 4};
// 配列内の全要素の並びを逆順にする
Array.Reverse(arr);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// 配列内のインデックス2から3個分の要素の並びを逆順にする
Array.Reverse(arr, 2, 3);
foreach (var elem in arr) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr() As Integer = {0, 1, 2, 3, 4}
' 配列内の全要素の並びを逆順にする
Array.Reverse(arr)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' 配列内のインデックス2から3個分の要素の並びを逆順にする
Array.Reverse(arr, 2, 3)
For Each elem As Integer In arr
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
4, 3, 2, 1, 0, 4, 3, 0, 1, 2,
ジャグ配列をリバースすることも出来ますが、その場合は1段目のみが逆順になります。 2段目以降をリバースする場合は、格納されている配列に対して個別にArray.Reverseメソッドを呼び出してリサイズする必要があります。
なお、Array.Reverseメソッドでは多次元配列を逆順にすることは出来ません。 Array.Reverseメソッドに多次元配列を指定すると、RankExceptionがスローされます。
ソート (Sort)
Array.Sortメソッドを使うことで配列内の全要素をソートして昇順に並べ替えることが出来ます。 ソートする最初のインデックスと要素数を指定することで、配列内の一部分だけをソートすることも出来ます。
using System;
class Sample {
static void Main()
{
int[] arr1 = {4, 2, 0, 3, 1};
// 配列内の全要素を昇順に並べ替える
Array.Sort(arr1);
foreach (var elem in arr1) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
int[] arr2 = {4, 3, 2, 1, 0};
// 配列内のインデックス2から3個分の要素を昇順に並べ替える
Array.Sort(arr2, 2, 3);
foreach (var elem in arr2) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr1() As Integer = {4, 2, 0, 3, 1}
' 配列内の全要素を昇順に並べ替える
Array.Sort(arr1)
For Each elem As Integer In arr1
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
Dim arr2() As Integer = {4, 3, 2, 1, 0}
' 配列内のインデックス2から3個分の要素を昇順に並べ替える
Array.Sort(arr2, 2, 3)
For Each elem As Integer In arr2
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 2, 3, 4, 4, 3, 0, 1, 2,
Array.Sortメソッドを使ったソートについてより詳しくは基本型のソートと昇順・降順でのソートで解説しています。 また、Array.Sortメソッドを使って多次元配列やジャグ配列をソートする方法についてはジャグ配列・多次元配列のソートで詳しく解説しています。
シャッフル
Arrayクラスには配列をランダムな順に並べ替える、つまりシャッフルを行うメソッドは用意されていません。 配列をシャッフルするには乱数を使ってソートするなどの方法で独自に実装する必要があります。
配列をシャッフルする具体的な実装例は配列・コレクションのシャッフルで紹介しています。
要素の検索
ここでは配列内にある要素の検索を行うメソッドについて紹介します。 なお、配列の型によっては引数でIComparerなどのインターフェイスが必要になりますが、ここではそういった事項に付いては触れません。 必要に応じて大小関係の定義と比較や等価性の定義と比較を参照してください。
要素の位置の検索 (IndexOf, LastIndexOf, BinarySearch)
Array.IndexOfメソッドを使うことで、配列内における指定した値を持つ要素の位置を検索して取得することが出来ます。
このメソッドは配列の前方から検索を行うため、配列内に同一の値を持つ要素がある場合は、そのうちより小さい(最初に見つかった)インデックスを返します。 逆に、Array.LastIndexOfメソッドは配列の後方から検索を行うため、より大きい(前方から見て最後に見つかった)インデックスを返します。 どちらのメソッドも、配列内に該当する要素が無い場合は-1が返されます。
なお、Array.IndexOf, Array.LastIndexOfメソッドでは多次元配列内の要素の位置を検索することは出来ません。 メソッドに多次元配列を指定すると、RankExceptionがスローされます。
using System;
class Sample {
static void Main()
{
int[] arr = {0, 1, 0, 2, 0, 3};
// 値に0が格納されている最初のインデックスを取得
Console.WriteLine("IndexOf(0) = {0}", Array.IndexOf(arr, 0));
// 値に0が格納されている最後のインデックスを取得
Console.WriteLine("LastIndexOf(0) = {0}", Array.LastIndexOf(arr, 0));
// 値に4が格納されている最初のインデックスを取得
Console.WriteLine("IndexOf(4) = {0}", Array.IndexOf(arr, 4));
}
}
Imports System
Class Sample
Shared Sub Main()
Dim arr() As Integer = {0, 1, 0, 2, 0, 3}
' 値に0が格納されている最初のインデックスを取得
Console.WriteLine("IndexOf(0) = {0}", Array.IndexOf(arr, 0))
' 値に0が格納されている最後のインデックスを取得
Console.WriteLine("LastIndexOf(0) = {0}", Array.LastIndexOf(arr, 0))
' 値に4が格納されている最初のインデックスを取得
Console.WriteLine("IndexOf(4) = {0}", Array.IndexOf(arr, 4))
End Sub
End Class
IndexOf(0) = 0 LastIndexOf(0) = 4 IndexOf(4) = -1
Array.BinarySearchメソッドを使うことでも配列内における要素の位置を検索出来ます。
Array.IndexOfメソッドは先頭から順に1つずつ要素を検証していくのに対し、Array.BinarySearchメソッドは二分探索によって要素を検索するため、より高速に要素の位置を検索できます(§.IndexOfとBinarySearchの速度比較)。 ただし、いくつかの点でArray.IndexOfメソッドとは異なるため、単純にArray.IndexOfメソッドの代わりとしてArray.BinarySearchメソッドを適用できるとは限りません。
まず、Array.BinarySearchメソッドを使って要素の位置を検索する場合、配列の要素が昇順に並んでいる必要があります。 そのため、Array.BinarySearchメソッドを使う場合は、あらかじめ昇順に並んでいる配列に対して使うか、呼び出す前にあらかじめ配列をソートしておくようにします。 昇順に並んでいない配列に対してArray.BinarySearchメソッドを使った場合でも、例外はスローされず、得られる結果も正しいものにはなりません。
次に、Array.BinarySearchメソッドは該当する要素が見つかった場合はそのインデックスを返しますが、Array.IndexOfメソッドとは異なり、該当する要素が複数ある場合でもそのうちもっとも小さいインデックスが返されるとは限りません。 また、配列内に該当する要素が無い場合は負の数が返されます(-1が返されるとは限りません)。
using System;
class Sample {
static void Main()
{
// 要素が昇順に並んでいる配列
int[] arr = {0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5};
// IndexOfメソッドで値に2が格納されているインデックスを取得
Console.WriteLine("IndexOf(2) = {0}", Array.IndexOf(arr, 2));
// BinarySearchメソッドで値に2が格納されているインデックスを取得
Console.WriteLine("BinarySearch(2) = {0}", Array.BinarySearch(arr, 2));
// 値に7が格納されているインデックスを取得
Console.WriteLine("IndexOf(7) = {0}", Array.IndexOf(arr, 7));
Console.WriteLine("BinarySearch(7) = {0}", Array.BinarySearch(arr, 7));
}
}
Imports System
Class Sample
Shared Sub Main()
' 要素が昇順に並んでいる配列
Dim arr() As Integer = {0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5}
' IndexOfメソッドで値に2が格納されているインデックスを取得
Console.WriteLine("IndexOf(2) = {0}", Array.IndexOf(arr, 2))
' BinarySearchメソッドで値に2が格納されているインデックスを取得
Console.WriteLine("BinarySearch(2) = {0}", Array.BinarySearch(arr, 2))
' 値に7が格納されているインデックスを取得
Console.WriteLine("IndexOf(7) = {0}", Array.IndexOf(arr, 7))
Console.WriteLine("BinarySearch(7) = {0}", Array.BinarySearch(arr, 7))
End Sub
End Class
IndexOf(2) = 3 BinarySearch(2) = 5 IndexOf(7) = -1 BinarySearch(7) = -13
なお、Array.BinarySearchメソッドもArray.IndexOfメソッドと同様、多次元配列内の要素の位置を検索することは出来ません。 メソッドに多次元配列を指定すると、RankExceptionがスローされます。
IndexOfとBinarySearchの速度比較
要素数nの配列に対して、IndexOfの速度はO(n)であるのに対し、BinarySearchはO(log2n)となります。
参考までに、Array.IndexOfメソッドとArray.BinarySearchメソッドを使った場合の速度の違いを比較した結果を紹介します。 次のように、IndexOfメソッドを使った検索では配列の要素数が増えるにつれ多大な時間がかかるようになっていますが、BinarySearchメソッドを使った検索では配列の要素数が増えても比較的短い時間で結果が得られています。
Length = 100 IndexOf: 00:00:00.0001335 BinarySearch: 00:00:00.0003081 IndexOf: 00:00:00.0000226 BinarySearch: 00:00:00.0000201 IndexOf: 00:00:00.0000257 BinarySearch: 00:00:00.0000195
Length = 10000 IndexOf: 00:00:00.1274312 BinarySearch: 00:00:00.0033034 IndexOf: 00:00:00.1212156 BinarySearch: 00:00:00.0018985 IndexOf: 00:00:00.1183027 BinarySearch: 00:00:00.0019239
Length = 100000 IndexOf: 00:00:12.8952917 BinarySearch: 00:00:00.0278454 IndexOf: 00:00:12.9067753 BinarySearch: 00:00:00.0270422 IndexOf: 00:00:13.0714185 BinarySearch: 00:00:00.0276836
using System;
using System.Diagnostics;
class Sample {
static void Main()
{
// ソート済みの要素を含む配列を作成
var arr = new int[100 * 1000];
for (var i = 0; i < arr.Length; i++) {
arr[i] = i;
}
Console.WriteLine("Length = {0}", arr.Length);
// 3回試行
for (var c = 0; c < 3; c++) {
var tick = Environment.TickCount;
// IndexOfメソッドでの探索
var rand1 = new Random(tick);
var sw1 = Stopwatch.StartNew();
for (var n = 0; n < arr.Length; n++) {
Array.IndexOf(arr, rand1.Next(0, arr.Length));
}
sw1.Stop();
Console.WriteLine("IndexOf: {0}", sw1.Elapsed);
// BinarySearchメソッドでの探索
var rand2 = new Random(tick);
var sw2 = Stopwatch.StartNew();
for (var n = 0; n < arr.Length; n++) {
Array.BinarySearch(arr, rand2.Next(0, arr.Length));
}
sw2.Stop();
Console.WriteLine("BinarySearch: {0}", sw2.Elapsed);
}
}
}
述語を使った検索 (Find, Exists, etc.)
Arrayクラスには、述語(Predicate)を使った要素の検索や取得を行うメソッドがいくつか用意されています。 デリゲートとして条件を記述したものをこれらのメソッドに渡すことによって、その条件に見合う要素を検索・取得することが出来ます。
これらのメソッドを使うことにより、for文・foreach文で列挙してif文で各要素を条件と照らし合わせて検索するといった処理を、メソッドを呼び出すだけで済ませる事が出来るようになります。 言い換えるとfor文・foreach文による配列要素に対する繰り返し処理から、if文等による条件分岐等の処理をメソッド/デリゲートとして分離することができます。
Arrayクラスには、述語を使った要素の検索・取得を行うことが出来るメソッドとして次のようなものが用意されています。
メソッド | 機能 |
---|---|
Find | 述語で定義された条件と一致する最初の要素を取得する |
FindLast | 述語で定義された条件と一致する最後の要素を取得する |
FindAll | 述語で定義された条件と一致するすべての要素を取得する |
FindIndex | 述語で定義された条件と一致する最初の要素のインデックスを取得する |
FindLastIndex | 述語で定義された条件と一致する最後の要素のインデックスを取得する |
Exists | 述語で定義された条件と一致する要素があるかどうかを判断する |
TrueForAll | 述語で定義された条件とすべての要素が合致するかどうかを判断する |
次の例では、値が偶数かどうかを返すメソッドを作成し、それを述語として使用することで配列内にある偶数の要素を検索・取得しています。
using System;
class Sample {
// 引数で与えられた数が偶数かどうか
static bool IsEven(int val)
{
if (val % 2 == 0)
return true;
else
return false;
}
static void Main()
{
int[] arr = {1, 4, 3, 5, 2, 6};
// 「値が偶数かどうか」という述語となるデリゲート
Predicate<int> isEven = IsEven;
// 値が偶数の要素とそのインデックスを取得
Console.WriteLine("Find: {0}", Array.Find(arr, isEven));
Console.WriteLine("FindIndex: {0}", Array.FindIndex(arr, isEven));
Console.WriteLine("FindLast: {0}", Array.FindLast(arr, isEven));
Console.WriteLine("FindLastIndex: {0}", Array.FindLastIndex(arr, isEven));
// 値が偶数の要素をすべて取得
Console.Write("FindAll: ");
foreach (var elem in Array.FindAll(arr, isEven)) {
Console.Write("{0}, ", elem);
}
Console.WriteLine();
// 値が偶数の要素が存在するかどうか
Console.WriteLine("Exists? {0}", Array.Exists(arr, isEven));
// すべての要素の値が偶数かどうか
Console.WriteLine("TrueForAll? {0}", Array.TrueForAll(arr, isEven));
}
}
Imports System
Class Sample
' 引数で与えられた数が偶数かどうか
Shared Function IsNumberEven(ByVal val As Integer) As Boolean
If val Mod 2 = 0 Then
Return True
Else
Return False
End If
End Function
Shared Sub Main()
Dim arr() As Integer = {1, 4, 3, 5, 2, 6}
' 「値が偶数かどうか」という述語となるデリゲート
Dim isEven As Predicate(Of Integer) = AddressOf IsNumberEven
' 値が偶数の要素とそのインデックスを取得
Console.WriteLine("Find: {0}", Array.Find(arr, isEven))
Console.WriteLine("FindIndex: {0}", Array.FindIndex(arr, isEven))
Console.WriteLine("FindLast: {0}", Array.FindLast(arr, isEven))
Console.WriteLine("FindLastIndex: {0}", Array.FindLastIndex(arr, isEven))
' 値が偶数の要素をすべて取得
Console.Write("FindAll: ")
For Each elem As Integer In Array.FindAll(arr, isEven)
Console.Write("{0}, ", elem)
Next
Console.WriteLine()
' 値が偶数の要素が存在するかどうか
Console.WriteLine("Exists? {0}", Array.Exists(arr, isEven))
' すべての要素の値が偶数かどうか
Console.WriteLine("TrueForAll? {0}", Array.TrueForAll(arr, isEven))
End Sub
End Class
Find: 4 FindIndex: 1 FindLast: 6 FindLastIndex: 5 FindAll: 4, 2, 6, Exists? True TrueForAll? False
全要素の変換 (ConvertAll)
Array.ConvertAllメソッドを使うことで、配列内の全要素の値を変換することが出来ます。 このメソッドでは、変換処理を記述したメソッドをConverterデリゲートの形式で指定することで、その処理を配列内の全要素に適用し、その結果を格納した配列を取得することが出来ます。
このメソッドを使うことにより、ある配列を別の型の配列に変換したり、全要素に同じ関数を適用して値を変換する事が出来ます。
using System;
class Sample {
// 文字列を数値に変換するメソッド
static int ToInt(string str)
{
return int.Parse(str); // 数値として不正な文字列等の処理については省略
}
static void Main()
{
string[] strings = {"0", "1", "2", "3"};
// 文字列型配列内のすべての要素を数値に変換する
var ints = Array.ConvertAll(strings, ToInt);
foreach (var i in ints) {
Console.Write("{0}, ", i);
}
Console.WriteLine();
}
}
Imports System
Class Sample
' 文字列を数値に変換するメソッド
Shared Function ToInt(ByVal str As String) As Integer
Return Integer.Parse(str) ' 数値として不正な文字列等の処理については省略
End Function
Shared Sub Main()
Dim strings() As String = {"0", "1", "2", "3"}
' 文字列型配列内のすべての要素を数値に変換する
Dim ints() As Integer = Array.ConvertAll(strings, AddressOf ToInt)
For Each i As Integer In ints
Console.Write("{0}, ", i)
Next
Console.WriteLine()
End Sub
End Class
0, 1, 2, 3,
using System;
class Sample {
// 度数を弧度に変える
static double ToRadian(double degree)
{
return degree * Math.PI / 180.0;
}
static void Main()
{
double[] degrees = {0.0, 90.0, 180.0, 270.0};
// 配列内の値をすべて弧度に変換する
var radians = Array.ConvertAll(degrees, ToRadian);
foreach (var rad in radians) {
Console.Write("{0}, ", rad);
}
Console.WriteLine();
// 配列を文字列の配列に変換して連結する
Console.WriteLine(string.Join(", ", Array.ConvertAll(degrees, Convert.ToString)));
}
}
Imports System
Class Sample
' 度数を弧度に変える
Shared Function ToRadian(ByVal degree As Double) As Double
Return degree * Math.PI / 180.0
End Function
Shared Sub Main()
Dim degrees() As Double = {0.0, 90.0, 180.0, 270.0}
' 配列内の値をすべて弧度に変換する
Dim radians() As Double = Array.ConvertAll(degrees, AddressOf ToRadian)
For Each rad As Double In radians
Console.Write("{0}, ", rad)
Next
Console.WriteLine()
' 配列を文字列の配列に変換して連結する
Console.WriteLine(String.Join(", ", Array.ConvertAll(degrees, AddressOf Convert.ToString)))
End Sub
End Class
0, 1.5707963267949, 3.14159265358979, 4.71238898038469, 0, 90, 180, 270
要素の列挙 (ForEach)
Array.ForEachメソッドを使うと、配列の要素を列挙してすべての要素に対して同一の処理を施すことが出来ます。 このメソッドでは、処理を記述したメソッドをActionデリゲートの形式で指定することで、配列内の個々の要素を引数としてそのメソッドが呼び出されます。 foreach文の内側でメソッド呼び出しを行う代わりに、このメソッドを使ってより簡単に記述することが出来ます。
using System;
class Sample {
// 引数の値をゼロ埋めして表示
static void Print(int val)
{
Console.WriteLine("{0:D3}", val);
}
static void Main()
{
int[] arr = {0, 1, 2, 3, 4};
// 配列内の値をゼロ埋めして表示
Array.ForEach(arr, Print);
Console.WriteLine();
// 上記の処理をforeach文で記述した場合
foreach (var elem in arr) {
Print(elem);
}
}
}
Imports System
Class Sample
' 引数の値をゼロ埋めして表示
Shared Sub Print(ByVal val As Integer)
Console.WriteLine("{0:D3}", val)
End Sub
Shared Sub Main()
Dim arr() As Integer = {0, 1, 2, 3, 4}
' 配列内の値をゼロ埋めして表示
Array.ForEach(arr, AddressOf Print)
Console.WriteLine()
' 上記の処理をFor Eachステートメントで記述した場合
For Each elem As Integer In arr
Print(elem)
Next
End Sub
End Class
000 001 002 003 004 000 001 002 003 004
このメソッドは配列内の各要素を列挙するだけであるため、元の配列に変更を加えるような処理を行うことは出来ません。 そういった処理を行いたい場合はfor文やArray.ConvertAllメソッドを使います。