.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)となります。
どのような値で初期化されるか、型とデフォルト値の詳細については型の種類・サイズ・精度・値域 §.型のデフォルト値を参照してください。
また、このメソッドは引数としてクリアを開始するインデックスと長さをとるため、配列の一部分だけをクリアすることも出来ます。
なお、Array.Clearメソッドは配列内の要素をクリアするためのもので、配列の長さを0にするものではありません。 配列の長さを0にするにはResizeメソッドを使います。
Array.Clearメソッドは、0を指定したmemset
、あるいはZeroMemory
マクロのような動作となります。 Array.Clearメソッドでは配列を0以外の値にクリアする事はできません。 配列全体を0以外の同一の値にセットする、memset
やFillMemory
マクロのような動作を求める場合は、Array.Fillメソッドを使用するか、for文等を使って個別に実装する必要があります。
ジャグ配列・多次元配列のクリア
Array.Clearメソッドでジャグ配列をクリアする場合は、ジャグ配列の1段目に格納されている配列がクリアされヌル参照(null/Nothing)の状態になります。 ジャグ配列に格納されている各配列はクリアされません。
一方、Array.Clearメソッドでは多次元配列をクリアすることも出来ますが、その際多次元配列は1次元配列の様に扱われます。 Array.Clearメソッドの引数に指定するインデックスは、多次元配列を1次元配列に展開したときの位置として解釈されるという点に注意が必要です。
同じ値で埋める (Fill)
Array.Fillメソッドを用いることで、配列内のすべての要素を同じ値に設定することができます。 Array.Clearメソッドが配列内すべてを0に設定するのに対して、Fillメソッドでは任意の値に設定することができます。 また、Clearメソッドと同様に、配列内の範囲を指定してその要素だけを同じ値に設定することもできます。 Array.Fillメソッドは.NET Standard 2.1/.NET Core 2.0以降で使用できます。
Array.Clearメソッドは二次元以上の配列にも対応していますが、Array.Fillメソッドは一次元配列のみに対応しています。
リサイズ (Resize)
Array.Resizeメソッドを使うことで、配列の長さを変更することが出来ます。 このメソッドによってリサイズされるというのは見た目の動作で、実際には、変更後のサイズの配列が新たに作成され、その配列に元の配列の内容をコピーしたものが引数で指定した配列と置き換えられるという動作になります。 このメソッドでは、配列の長さを元の長さよりも短くすることも長くすることも可能です。 Array.Resizeメソッドで配列を拡張した場合、拡張した部分にはデフォルト値がセットされます。
ジャグ配列をリサイズすることも出来ますが、その場合は1段目の長さのみが変更できます。 2段目以降の長さを変更する場合は、格納されている配列に対して個別にArray.Resizeメソッドを呼び出してリサイズする必要があります。
なお、Array.Resizeメソッドでは多次元配列のサイズを変更することは出来ません。
複製・複写
複製 (Clone)
Array.Cloneメソッドを使うことで、配列を複製して同じ内容を持った配列を生成することが出来ます。 Array.Cloneメソッドの戻り値はobjectであるため、必要に応じて複製元と同じ型にキャストして使います。
このメソッドでは簡易コピーが行われるため、参照型を要素に持つ配列では参照のみがコピーされます。 そのため、ジャグ配列の複製を行うと、複製元と複製後のジャグ配列内における2段目以降はどちらも同一のインスタンスを参照する事になります。
Cloneメソッドと簡易コピーについてより詳しくはオブジェクトの複製で解説しています。
多次元配列を複製する場合の結果は1次元配列を複製する場合と同様で、長さ・次元数・格納される要素が同一の多次元配列が生成されます。
複写 (Copy, CopyTo)
Copyメソッド
Array.Copyメソッドは、Array.Cloneメソッドとは異なり配列の内容を既に存在する別の配列に複写します。 引数で複写する長さ(要素数)を指定できるので、配列の途中までをコピーするということも出来ます。 当然、複写先の配列は複写元と同じがそれ以上の長さを持っている必要があります。 Array.Copyメソッドも、Array.Cloneメソッドと同様要素の簡易コピーを行います。
配列のバイト表現を別の配列へ転写する目的(バイト列としてのコピー)にはBuffer.BlockCopyメソッドを使うことができます。 詳しくはバイト列操作 §.BlockCopyメソッドを参照してください。
Array.Copyメソッドは、複写元と複写先に同じ配列を指定することができます。 またArray.Copyメソッドの動作は、memcpy
ではなくmemmove
相当の動作になります。 詳しくは§.同一配列への複写を参照してください。
Array.Copyメソッドでは、複写する長さだけでなく、複写元と複写先の始点となるインデックスを指定して複写することも出来ます。 Arrayクラスには部分配列を抜き出すArray.Sliceのようなメソッドは用意されていませんが、次の例のように配列の生成と部分配列の複写を行うことで、Array.Slice相当の処理を行うことが出来ます。
この方法では部分配列を取得するために新たな配列インスタンスの作成と複写を行うことになりますが、ArraySegmentを使えばインスタンス生成や複写を行わずに部分配列のビューを取得・作成することもできます。
C# 8.0以降、.NET Core 3.0以降では、インデックス/範囲構文n..m
を使うことで部分配列の抽出を行うことができるようになっています。
部分配列の取得と同様に、配列の連結を行うArray.Concatのようなメソッドも用意されていませんが、次の例のように連結後の配列の生成と連結元の配列の複写を行うことで、Array.Concat相当の処理を行うことが出来ます。
多次元配列の複写
多次元配列もArray.Copyメソッドで複写できます。 多次元配列を複写する場合は、次元数が同じで複写先の長さが十分であれば、複写元と複写先で次元毎の長さが異なっていても複写することが出来ます。
Copyメソッドでは次元数が異なる配列への複写が出来ないので、そういった場合はfor文・foreach文を使って要素を一つずつコピーする必要があります。
同一配列への複写
Array.Copyメソッドでは、複写先を複写元と同一の配列にすることが出来ます。 この際、複写先と複写元の一部が重なっていても問題なく複写されます。
このように、Array.Copyメソッドはmemcpy
ではなくmemmove
に相当する動作となります。
異なる型の配列への複写
複写元と複写先の型に互換性がある場合、もしくは型が複写元と複写先がどちらもプリミティブ型で複写が拡大変換となる場合に限り、異なる型の配列へ複写することが出来ます。
例えば、次の例のように、ともにプリミティブ型であり拡大変換となるint型配列からlong型配列への複写は正常に行われます。
先に挙げた以外の場合、すなわち型に互換性がない、どちらもプリミティブ型でない、型の変換が縮小変換となるのいずれかの場合では、例外ArrayTypeMismatchExceptionがスローされます。
例えば、上記の例を逆にしてlong型配列からint型配列に複写しようとする場合、どちらの型もプリミティブではあってもlongからintへは縮小変換となるため、ArrayTypeMismatchExceptionがスローされます。
型の分類、プリミティブ型については型の種類・サイズ・精度・値域、拡大変換・縮小変換については基本型の型変換で詳しく解説しています。
Buffer.BlockCopyメソッドでは、プリミティブ型であれば縮小変換となるような配列へのコピーも行うことができます。 詳しくはバイト列操作 §.BlockCopyメソッドを参照してください。
CopyToメソッド
Array.Copyメソッドの他にも、Array.CopyToメソッドを使って複写することも出来ます。 このメソッドはArray.Copyとは異なりインスタンスメソッドです。 また、引数に指定するのは複写先の始点となるインデックスで、常に複写元の配列全体が複写されます。
CopyToはインスタンスメソッドであるという点以外は、Copyメソッドと同様で、その動作もCopyメソッドと同じです。
配列の作成・要素の取得と設定 (CreateInstance, GetValue, SetValue)
言語で用意されている構文を使う他に、Array.CreateInstanceメソッドを使うことで任意の型・任意の長さの配列を作成することが出来ます。 このメソッドの戻り値はArrayクラスとなるので、必要に応じて適切な配列型にキャストしてから使います。 このメソッドで作成した配列は、通常の構文で作成した配列と何ら変わりありません。
また、Array.GetValueメソッドおよびArray.SetValueメソッドを使うことで、配列の各要素にアクセスするための構文(インデクサ)を使用せずに配列に値を取得・設定することが出来ます。 このメソッドは配列の型・次元がどのようなものでも値を取得・設定することが出来ます。
GetValueメソッド・SetValueメソッドは要素単位で値の取得・設定を行います。 バイト単位で配列内の値を取得・設定したい場合はBuffer.GetByteメソッド・Buffer.SetByteメソッドを使うことができます。 詳しくはバイト列操作 §.バイト単位での値の設定・取得 (GetByteメソッド・SetByteメソッド)を参照してください。
Array.CreateInstance, Array.GetValue, Array.SetValueの各メソッドは、多次元配列でも使うことが出来ます。 次の例では、Array.CreateInstanceメソッドで指定された型の行列を表す2次元配列を作成し、Array.SetValueメソッドで値を設定して単位行列を構成しています。
読み取り専用化 (AsReadOnly)
Array.AsReadOnlyメソッドを使うことで、配列を元に読み取り専用のコレクションを作成することが出来ます。
Array.AsReadOnlyメソッドの戻り値はReadOnlyCollectionで、これは読み取り専用にした配列のように振る舞います。 例えば、インデックスを指定して要素に値を設定しようとするとコンパイルエラーとなり、IList等のインターフェイスにキャストして無理やり変更しようとしてもNotSupportedExceptionがスローされます。
それ以外の操作、例えば要素の参照や値の列挙は、通常の配列と同様に振る舞います。 なお、通常の配列と異なりReadOnlyCollectionにはLengthプロパティが無いので、代わりにCountプロパティを参照します。
ReadOnlyCollectionについてより詳しくは汎用ジェネリックコレクション(1) Collection/ReadOnlyCollection §.ReadOnlyCollectionを参照してください。
Array.AsReadOnlyメソッドは読み取り専用のラッパーを作成するだけであり、元となった配列が読み取り専用となるのではないため、元の配列に対しては依然として値を設定することが可能です。 元の配列を変更すると、作成したReadOnlyCollectionにもその変更が反映されます。
要素の並べ替え
リバース (Reverse)
Array.Reverseメソッドを使うことで配列内の全要素の並びを逆順に(リバース)することが出来ます。 逆順にする最初のインデックスと要素数を指定することで、配列内の一部分だけを逆順にすることも出来ます。
ジャグ配列をリバースすることも出来ますが、その場合は1段目のみが逆順になります。 2段目以降をリバースする場合は、格納されている配列に対して個別にArray.Reverseメソッドを呼び出してリサイズする必要があります。
なお、Array.Reverseメソッドでは多次元配列を逆順にすることは出来ません。 Array.Reverseメソッドに多次元配列を指定すると、RankExceptionがスローされます。
ソート (Sort)
Array.Sortメソッドを使うことで配列内の全要素をソートして昇順に並べ替えることが出来ます。 ソートする最初のインデックスと要素数を指定することで、配列内の一部分だけをソートすることも出来ます。
Array.Sortメソッドを使ったソートについてより詳しくは基本型のソートと昇順・降順でのソートで解説しています。 また、Array.Sortメソッドを使って多次元配列やジャグ配列をソートする方法についてはジャグ配列・多次元配列のソートで詳しく解説しています。
シャッフル
Arrayクラスには配列をランダムな順に並べ替える、つまりシャッフルを行うメソッドは用意されていません。 配列をシャッフルするには乱数を使ってソートするなどの方法で独自に実装する必要があります。
配列をシャッフルする具体的な実装例は配列・コレクションのシャッフルで紹介しています。
要素の検索
ここでは配列内にある要素の検索を行うメソッドについて紹介します。 なお、配列の型によっては引数でIComparerなどのインターフェイスが必要になりますが、ここではそういった事項に付いては触れません。 必要に応じて大小関係の定義と比較や等価性の定義と比較を参照してください。
要素の位置の検索 (IndexOf, LastIndexOf, BinarySearch)
Array.IndexOfメソッドを使うことで、配列内における指定した値を持つ要素の位置を検索して取得することが出来ます。
このメソッドは配列の前方から検索を行うため、配列内に同一の値を持つ要素がある場合は、そのうちより小さい(最初に見つかった)インデックスを返します。 逆に、Array.LastIndexOfメソッドは配列の後方から検索を行うため、より大きい(前方から見て最後に見つかった)インデックスを返します。 どちらのメソッドも、配列内に該当する要素が無い場合は-1が返されます。
なお、Array.IndexOf, Array.LastIndexOfメソッドでは多次元配列内の要素の位置を検索することは出来ません。 メソッドに多次元配列を指定すると、RankExceptionがスローされます。
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が返されるとは限りません)。
なお、Array.BinarySearchメソッドもArray.IndexOfメソッドと同様、多次元配列内の要素の位置を検索することは出来ません。 メソッドに多次元配列を指定すると、RankExceptionがスローされます。
IndexOfとBinarySearchの速度比較
要素数nの配列に対して、IndexOfの速度はO(n)であるのに対し、BinarySearchはO(log2n)となります。
参考までに、Array.IndexOfメソッドとArray.BinarySearchメソッドを使った場合の速度の違いを比較した結果を紹介します。 次のように、IndexOfメソッドを使った検索では配列の要素数が増えるにつれ多大な時間がかかるようになっていますが、BinarySearchメソッドを使った検索では配列の要素数が増えても比較的短い時間で結果が得られています。
述語を使った検索 (Find, Exists, etc.)
Arrayクラスには、述語(Predicate)を使った要素の検索や取得を行うメソッドがいくつか用意されています。 デリゲートとして条件を記述したものをこれらのメソッドに渡すことによって、その条件に見合う要素を検索・取得することが出来ます。
これらのメソッドを使うことにより、for文・foreach文で列挙してif文で各要素を条件と照らし合わせて検索するといった処理を、メソッドを呼び出すだけで済ませる事が出来るようになります。 言い換えるとfor文・foreach文による配列要素に対する繰り返し処理から、if文等による条件分岐等の処理をメソッド/デリゲートとして分離することができます。
Arrayクラスには、述語を使った要素の検索・取得を行うことが出来るメソッドとして次のようなものが用意されています。
メソッド | 機能 |
---|---|
Find | 述語で定義された条件と一致する最初の要素を取得する |
FindLast | 述語で定義された条件と一致する最後の要素を取得する |
FindAll | 述語で定義された条件と一致するすべての要素を取得する |
FindIndex | 述語で定義された条件と一致する最初の要素のインデックスを取得する |
FindLastIndex | 述語で定義された条件と一致する最後の要素のインデックスを取得する |
Exists | 述語で定義された条件と一致する要素があるかどうかを判断する |
TrueForAll | 述語で定義された条件とすべての要素が合致するかどうかを判断する |
次の例では、値が偶数かどうかを返すメソッドを作成し、それを述語として使用することで配列内にある偶数の要素を検索・取得しています。
全要素の変換 (ConvertAll)
Array.ConvertAllメソッドを使うことで、配列内の全要素の値を変換することが出来ます。 このメソッドでは、変換処理を記述したメソッドをConverterデリゲートの形式で指定することで、その処理を配列内の全要素に適用し、その結果を格納した配列を取得することが出来ます。
このメソッドを使うことにより、ある配列を別の型の配列に変換したり、全要素に同じ関数を適用して値を変換する事が出来ます。
要素の列挙 (ForEach)
Array.ForEachメソッドを使うと、配列の要素を列挙してすべての要素に対して同一の処理を施すことが出来ます。 このメソッドでは、処理を記述したメソッドをActionデリゲートの形式で指定することで、配列内の個々の要素を引数としてそのメソッドが呼び出されます。 foreach文の内側でメソッド呼び出しを行う代わりに、このメソッドを使ってより簡単に記述することが出来ます。
このメソッドは配列内の各要素を列挙するだけであるため、元の配列に変更を加えるような処理を行うことは出来ません。 そういった処理を行いたい場合はfor文やArray.ConvertAllメソッドを使います。