配列の操作
Arrayクラス
配列は複数の同じデータ型をまとめて扱う機能です。
単純に複数の変数がひとつにまとめられるだけでなく、様々な便利な機能が提供されています。
ここでは配列操作でよく使用されるメソッド(関数)を解説します。
配列の操作のためのメソッドはArrayというクラスに存在します。
クラスと言うのは、ある機能を実現するためデータ群(変数や関数など)をひとつにまとめたものです。
なお、配列についての基本的な事柄は 配列、多次元配列の項を参照してください。
以下の説明でArray.○○
の形式のものは静的メソッドです。
メソッド名から始まるものはインスタンスメソッドです。
(→静的メソッドとインスタンスメソッド)
配列の基本
配列のページでも説明している基本的な事柄を簡単におさらいしておきます。
//配列の宣言(要素数3)
int[] nums1 = new int[3];
//宣言と同時に初期化
int[] nums2 = new int[] { 1, 2, 3 };
//宣言と同時に初期化(省略形)
int[] nums3 = { 1, 2, 3 };
//各要素へのアクセス
int num = nums2[0]; //1
//要素数の取得
int length = nums2.Length; //3
ジャグ配列の場合、これは「配列の配列」なので、メソッドが適用されるのは一次元目の要素、つまり「配列」に適用されます。
ジャグ配列が管理する「値」をメソッドの適用対象にする場合は、目的の配列を取得してからさらにその配列にメソッドを適用する必要があります。
配列サイズ
Array.Resize(要素数の変更)
Array.Resize
メソッドは、配列の要素数を変更します。
int[] nums = new int[] { 1, 2, 3 };
//要素数を5に変更
Array.Resize(ref nums, 5);
foreach (var n in nums)
Console.Write("{0}, ", n);
Console.WriteLine();
//要素数を2に変更
Array.Resize(ref nums, 2);
foreach (var n in nums)
Console.Write("{0}, ", n);
1, 2, 3, 0, 0, 1, 2,
第一引数には要素数を変更したい配列を指定します。
この時、ref
キーワードを指定する必要があります。
(ref
キーワードについては引数の参照渡しを参照)
第二引数には変更後の要素数を指定します。
変更前よりも要素数を増やした場合、新しい要素には既定値が設定されます。
変更前よりも要素数を減らした場合は切り捨てられます。
多次元配列の場合
Array.Resize
メソッドは多次元配列に適用することはできません。
(コンパイルエラー)
GetLength(要素数の取得)
GetLength
メソッドは、配列の指定の次元の要素数を取得します。
int[] arr1 = new int[] { 1, 2, 3 };
int[,] arr2 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
Console.WriteLine(arr1.GetLength(0));
Console.WriteLine(arr2.GetLength(0));
Console.WriteLine(arr2.GetLength(1));
3 3 2
第一引数には取得したい次元の番号を指定します。
(一次元目は0)
戻り値は要素数です。
一次元配列の場合は常に0を指定します。
二次元配列の場合は0か1を指定します。
次元数以上の値、または0未満を指定すると実行時エラーになります。
GetLongLength(要素数の取得)
GetLength
メソッドと似た動作をするものにGetLongLength
メソッドがあります。
GetLength
メソッドの戻り値はint型ですが、GetLongLength
メソッドはlong型(64ビット整数)で返します。
それ以外の違いはありません。
GetLowerBound(要素の開始番号の取得)
GetUpperBound(要素の終了番号の取得)
GetLowerBound
メソッドは、指定の次元の要素の開始番号(下限)を取得します。
GetUpperBound
メソッドは、指定の次元の要素の終了番号(上限)を取得します。
int[] arr1 = new int[] { 1, 2, 3 };
Console.Write("arr1[{0}]", arr1.GetLowerBound(0));
Console.WriteLine(" ~ arr1[{0}]", arr1.GetUpperBound(0));
int[,] arr2 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
Console.Write("arr2[{0}][{1}]",
arr2.GetLowerBound(0),
arr2.GetLowerBound(1));
Console.WriteLine(" ~ arr2[{0}][{1}]",
arr2.GetUpperBound(0),
arr2.GetUpperBound(1));
arr1[0] ~ arr1[2] arr2[0][0] ~ arr2[2][1]
第一引数は下限/上限を取得したい次元数を指定します。
次元数以上の値、または0未満を指定すると実行時エラーになります。
このコードはそれぞれの配列で使用可能な要素番号の範囲を表示しています。
通常の配列の開始番号は0なので、GetLowerBound
メソッドはほとんどの場合で0を返しますが、.NETは開始番号が0以外の配列もサポートしているためこのようなメソッドがあります。
要素全体への操作
Array.Clear(要素のクリア)
Array.Clear
メソッドは、配列の要素をすべて既定値に設定します。
int[] nums = new int[] { 1, 2, 3, 4, 5 };
//配列をすべてクリア
Array.Clear(nums);
foreach (var n in nums)
Console.Write("{0}, ", n);
Console.WriteLine();
nums = new int[] { 1, 2, 3, 4, 5 };
//1番目の要素から2つ分をクリア
Array.Clear(nums, 1, 2);
foreach (var n in nums)
Console.Write("{0}, ", n);
0, 0, 0, 0, 0, 1, 0, 0, 4, 5,
配列のみを引数に指定すると、その配列のすべての要素が既定値に設定されます。
第二引数に開始番号、第三引数に要素数を指定することで部分的に値をクリアすることもできます。
多次元配列の場合
多次元配列に対してArray.Clear
メソッドを適用することもできます。
クリアする範囲は多次元配列を一次元配列とみなして指定します。
int[,] nums = new int[,] {
{ 0, 1, 2 },
{ 3, 4, 5 },
{ 6, 7, 8 },
};
//すべてクリア
Array.Clear(nums, 0, nums.Length);
//Array.Clear(nums);でも良い
nums = new int[,] {
{ 0, 1, 2 },
{ 3, 4, 5 },
{ 6, 7, 8 },
};
//2番目から5つ分をクリア
Array.Clear(nums, 2, 5);
foreach (var n in nums)
Console.Write("{0}, ", n);
0, 1, 0, 0, 0, 0, 0, 7, 8,
Array.Fill(要素を埋める)
Array.Fill
メソッドは、配列の要素を指定の値で埋めます。
このメソッドは.NET Frameworkでは使用できません。
int[] nums = new int[] { 1, 2, 3, 4, 5 };
//全ての要素を6で埋める
Array.Fill(nums, 6);
foreach (var n in nums)
Console.Write("{0}, ", n);
Console.WriteLine();
//1番目の要素から2つ分を7で埋める
Array.Fill(nums, 7, 1, 2);
foreach (var n in nums)
Console.Write("{0}, ", n);
6, 6, 6, 6, 6, 6, 7, 7, 6, 6,
第一引数は対象の配列です。
第二引数は埋める要素です。
戻り値はありません。
第三引数に開始位置、第四引数に要素数を指定して、値を埋める範囲を指定することもできます。
多次元配列の場合
Array.Fill
メソッドは多次元配列に適用することはできません。
Array.ForEach(要素の列挙)
Array.ForEach
メソッドは、全要素に対して任意のメソッドを実行します。
static void Show(int n)
{
Console.WriteLine(n);
}
static void Main(string[] args)
{
int[] nums = new int[] { 1, 2, 3 };
Array.ForEach(nums, Show);
}
1 2 3
第一引数は対象となる配列です。
第二引数は適用するメソッド名を指定します。
(関数呼び出し演算子()
は記述しない)
メソッドは配列の要素の型ひとつを引数に取り、void型を返すように定義します。
このメソッドで配列の要素を書き換えることはできません。
要素を書き換える場合はfor文を使用してください。
ラムダ式による記述
Array.ForEach
メソッドのように、メソッドには他のメソッドを引数に取るものがあります。
これらはラムダ式という記法を用いると、その場に処理を記述することができます。
(別途メソッドを定義する必要がない)
int[] nums = new int[] { 1, 2, 3 };
Array.ForEach(nums, (n) => Console.WriteLine(n));
多次元配列の場合
Array.ForEach
メソッドは多次元配列に適用することはできません。
要素アクセス
GetValue(値の取得)
SetValue(値の書き込み)
GetValue
メソッドは指定の番号の要素の値を取得します。
SetValue
メソッドは指定の番号の要素に値を書き込みます。
int[] arr1 = new int[] { 1, 2, 3 };
Console.WriteLine(arr1.GetValue(0)); //0番目の要素
int[,] arr2 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
Console.WriteLine(arr2.GetValue(0, 1)); //[0, 1]の要素
Console.WriteLine(arr2.GetValue(new int[] { 2, 1 })); //[2, 1]の要素
Console.WriteLine();
arr1.SetValue(7, 0); //0番目の要素に7を代入
arr2.SetValue(8, 0, 1); //[0, 1]の要素に8を代入
arr2.SetValue(9, new int[] { 2, 1 }); //[2, 1]の要素に9を代入
foreach (int n in arr1)
Console.Write("{0} ", n);
Console.WriteLine();
foreach (int n in arr2)
Console.Write("{0} ", n);
1 2 6 7 2 3 1 8 3 4 5 9
GetValue
メソッドの第一引数は値を取得する要素番号です。
二次元配列の場合は第二引数で二次元目の要素番号を指定します。
要素番号を順番に記述した配列を渡すことでも要素を指定できます。
SetValue
メソッドは、第一引数に代入する値を指定します。
以降の引数はGetValue
メソッドと同じです。
配列は、通常は添字演算子[]
で要素アクセスを行えるため、これらのメソッドは特殊なケースでのみ使用します。
要素のコピー
CopyTo(配列のコピー)
CopyTo
メソッドは、配列の全ての要素を別の配列にコピーします。
int[] nums1 = new int[] { 1, 2, 3 };
int[] nums2 = new int[] { 4, 5, 6, 7 };
//nums1のすべてをnums2の1番目の要素からコピー
nums1.CopyTo(nums2, 1);
foreach(var n in nums2)
Console.Write("{0}, ", n);
4, 1, 2, 3,
コピー元配列のインスタンスメソッドとしてCopyTo
メソッドを実行します。
第一引数はコピー先となる配列です。
第二引数はコピー先の配列の何番目からコピーを開始するか(index)を指定します。
コピー先の配列の要素数が足りない場合はエラーになります。
(「index + コピー元の配列の要素数」よりもコピー先の配列の要素数が少ない場合)
また、indexにマイナス値を指定してもエラーになります。
CopyTo
メソッドはコピー元配列の要素全てがコピー対象です。
コピー先配列の何番目からコピーを開始するかを指定することはできますが、使い勝手がイマイチなので次に説明するArray.Copy
の使用を推奨します。
多次元配列の場合
CopyTo
メソッドは多次元配列には使用できません。
多次元配列のコピーは後述するArray.Copy
メソッドを使用します。
Array.Copy(配列のコピー)
Array.Copy
メソッドは、配列の要素の一部を別の配列にコピーします。
int[] nums1 = new int[] { 1, 2, 3 };
int[] nums2 = new int[] { 4, 5, 6, 7 };
//nums1の先頭から、nums2の先頭へ、要素数2つ分コピー
Array.Copy(nums1, nums2, 2);
foreach (var n in nums2)
Console.Write("{0}, ", n);
1, 2, 6, 7,
第一引数にコピー元、第二引数にコピー先となる配列を指定します。
第三引数はコピーする要素数を指定します。
コピーは先頭から行われます。
要素をすべてをコピーする場合は第三引数に配列の要素数を指定すれば良いですが、指定した値がコピー元もしくはコピー先の配列の要素数よりも大きい場合はエラー(例外)が発生します。
つまり、要素数の少ないほうに合わせる必要があります。
このメソッドは、コピー元配列のコピー開始番号と長さ、コピー先配列のコピー開始番号を指定してコピーすることもできます。
int[] nums1 = new int[] { 1, 2, 3 };
int[] nums2 = new int[] { 4, 5, 6, 7 };
//nums1の1番目の要素から、nums2の1番目の要素へ、要素数2つ分コピー
Array.Copy(nums1, 1, nums2, 1, 2);
foreach (var n in nums2)
Console.Write("{0}, ", n);
4, 2, 3, 7,
こちらは引数が多いですが柔軟なコピーが可能です。
多次元配列の場合
Array.Copy
メソッドは多次元配列のコピーも可能です。
ただしコピー元とコピー先は同じ次元の配列である必要があります。
(コピー元が二次元配列ならコピー先も二次元配列)
コピー先配列の要素数さえ足りていればそれぞれの次元の要素数は自由です。
コピーする範囲は多次元配列を一次元配列とみなして指定します。
int[,] nums = new int[3, 4] {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
int[,] nums2 = new int[2, 6];
//コピー先配が二次元配列で
//要素数が10以上あれば
//コピー可能
Array.Copy(nums, nums2, 10);
foreach (var n in nums2)
Console.Write("{0}, ", n);
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0,
Array.ConstrainedCopy(エラー時の値保証コピー)
Array.ConstrainedCopy
メソッドは、コピー処理が失敗した場合にコピー先配列の要素が書き換わらないことが保証されているコピーメソッドです。
int[] nums1 = new int[] { 1, 2, 3 };
int[] nums2 = new int[] { 4, 5, 6, 7 };
//nums1の1番目の要素から、nums2の1番目の要素へ、要素数2つ分コピー
//Array.Copy(nums1, 1, nums2, 1, 2);
Array.ConstrainedCopy(nums1, 1, nums2, 1, 2);
foreach (var n in nums2)
Console.Write("{0}, ", n);
4, 2, 3, 7,
引数はArray.Copy
メソッドのフルバージョンと同じです。
Array.Copy
をはじめとするコピーメソッドは、コピー処理中に何らかのエラーが発生し処理が中断した場合、コピー先配列には不完全なコピーが行われる可能性があります。
Array.ConstrainedCopy
メソッドはそのような場合に元の値に戻されることが保証されています。
Clone(配列の複製)
Clone
メソッドは配列を複製します。
int[] nums1 = new int[] { 1, 2, 3 };
//nums1の複製を作り、nums2に代入
//キャストが必要
int[] nums2 = nums1.Clone() as int[];
//こちらのキャストでも良い
//nums2 = (int[])nums1.Clone();
//キャストしない場合
object num3 = nums1.Clone();
foreach (var n in nums2)
Console.Write("{0}, ", n);
1, 2, 3,
あらゆるデータ型に対応するため、戻り値はobject型になっています。
そのためキャストして受け取る必要があります。
コピーメソッドの注意点
配列のコピーメソッド(CopyTo
やClone
など)が行うのはシャローコピーです。
シャローコピーは、要素が参照型の場合は参照先の値はコピーしません。
値型の配列の場合は問題ありませんが、参照型を要素とする配列の場合はコピー元と同じ先を参照するようになるので注意が必要です。
int[][] nums1 = new int[][] {
new int[] { 1, 2, 3 },
new int[] { 4, 5, 6 }
};
int[][] nums2 = nums1.Clone() as int[][];
//複製後に複製元の値を書き換え
nums1[0][0] = 9;
foreach (var first in nums1) //firstは配列のアドレス
foreach (var n in first)
Console.Write("{0}, ", n);
Console.WriteLine();
foreach (var first in nums2)
foreach (var n in first)
Console.Write("{0}, ", n);
9, 2, 3, 4, 5, 6, 9, 2, 3, 4, 5, 6,
ジャグ配列(配列の配列)の各先頭要素には配列型が入っています。
配列は参照型なので、Clone
メソッドを使用するとシャローコピーになります。
上記サンプルコードでは、配列の複製後に複製元にした配列の要素を書き換えています。
シャローコピーなので、複製先の配列の要素も書き変わっていることがわかります。
参照先も含めてコピーをしたい場合はシャローコピーとディープコピーの項を参照してください。
配列の変換
Array.ConvertAll(配列の変換)
Array.ConvertAll
メソッドは、すべての要素に任意のメソッドを適用した新しい配列を作成します。
static string Converter(int n)
{
return n.ToString();
}
static void Main(string[] args)
{
int[] nums = new int[] { 1, 2, 3 };
//全ての要素をstring型に変換した配列を取得
string[] strs = Array.ConvertAll(nums, Converter);
}
第一引数は対象となる配列を指定します。
第二引数は変換処理であるメソッド名を指定します。
(関数呼び出し演算子()
は記述しません)
メソッドは、対象となる配列の要素の型をひとつ引数にとり、任意のデータ型を返すように定義します。
戻り値はこのメソッドの戻り値の型の配列です。
このサンプルコードでは、int型配列nums
の要素を全てstring型に変換した新しい配列を作成しています。
戻り値のデータ型は自由なので、同じデータ型でも構いません。
多次元配列の場合
Array.ConvertAll
メソッドは多次元配列に適用することはできません。
(コンパイルエラー)
Array.AsReadOnly(読み取り専用コレクションの取得)
Array.AsReadOnly
メソッドは、配列の読み取り専用コレクションを取得します。
int[] nums = new int[] { 1, 2, 3 };
var roNums = Array.AsReadOnly(nums);
//書き換え不可
//roNums[0] = 9
返されるデータ型はSystem.Collections.ObjectModel.ReadOnlyCollection<T>
という非常に長いものです。
(T
は配列の要素のデータ型)
IReadOnlyList<T>
というデータ型でも受け取れますし、可能なら型推論で受け取りましょう。
なお、このメソッドは要素のコピーは行わず、元の配列の参照を返します。
元の配列の変更は読み取り専用コレクションにも影響します。
int[] nums = new int[] { 1, 2, 3 };
var roNums = Array.AsReadOnly(nums);
nums[0] = 9;
foreach (var n in roNums)
Console.Write("{0}, ", n);
9, 2, 3,
多次元配列の場合
Array.AsReadOnly
メソッドは多次元配列に適用することはできません。
GetEnumerator(IEnumeratorの取得)
GetEnumerator
メソッドは、配列のIEnumerator
オブジェクトを取得します。
int[] nums = new int[] { 1, 2, 3 };
System.Collections.IEnumerator enumerator = nums.GetEnumerator();
IEnumerator
に関してはyield文とIEnumerableの項を参照してください。
要素の並べ替え
Array.Sort(要素の並べ替え)
Array.Sort
メソッドは、要素を昇順で並べ替えます。
int[] nums = new int[] { 12, 16, 0, 7, 5 };
//並べ替え
Array.Sort(nums);
foreach (var n in nums)
Console.Write("{0}, ", n);
0, 5, 7, 12, 16,
降順に並べ替える場合はArray.Sort
メソッドを適用した後にArray.Reverse
メソッドを実行します。
以下のように、並べ替えの開始番号と要素数を指定することで、特定の範囲だけを並び替えすることもできます。
int[] nums = new int[] { 12, 16, 0, 7, 5 };
//1番目の要素から3つ分を並べ替え
Array.Sort(nums, 1, 3);
foreach (var n in nums)
Console.Write("{0}, ", n);
12, 0, 7, 16, 5,
キー配列と関連付けられた配列を同時に並べ替える
ある配列を並べ替えた後に、その並べ替え方を別の配列にも適用することができます。
int[] nums1 = { 1, 2, 5, 4, 3 };
int[] nums2 = { 5, 4, 3, 2, 1 };
Array.Sort(nums1, nums2);
foreach (var n in nums1)
Console.Write("{0}, ", n);
Console.WriteLine();
foreach (var n in nums2)
Console.Write("{0}, ", n);
1, 2, 3, 4, 5, 5, 4, 1, 2, 3,
まず、上記の配列num1
が昇順に並べ替えられます。
2番目と4番目の要素(先頭は0番目)が入れ替わることになりますが、この並べ替え方がもう一方の配列num2
にも適用されます。
第一引数の配列の要素数が第二引数の配列の要素数より多い場合はエラーになります。
メソッドを使用した並べ替え
Array.Sort
メソッドは並べ替え用のメソッドを使用することで、順序を任意に変更できます。
例えばstring型の並べ替えは通常はABC順(あいうえお順)ですが、以下のコードは「文字数が少ない順」「同じ文字数の場合はABC順」というルールで並べ替えをしています。
//Sortメソッド用の比較メソッド
//負数を返すとxが手前になる
//正数を返すとyが手前になる
//ゼロを返すと両者は同じ
static int CompareString(string x, string y)
{
//nullチェック
if (x == null && y == null)
return 0;
if (x == null)
return -1;
if (y == null)
return 1;
if (x.Length < y.Length)
return -1;
if (x.Length > y.Length)
return 1;
//文字数が同じ場合は通常通り
return string.Compare(x, y);
}
static void Main(string[] args)
{
var strs = new string[]
{ "Tom", "James", "Michael", "Chris", "Bobby" };
Array.Sort(strs);
Console.WriteLine("通常のソート");
foreach (var s in strs)
Console.Write("{0}, ", s);
Console.WriteLine();
//比較メソッドを使用して並べ替え
Array.Sort(strs, CompareString);
Console.WriteLine("独自ルールによるソート");
foreach (var s in strs)
Console.Write("{0}, ", s);
Console.ReadLine();
}
通常のソート Bobby, Chris, James, Michael, Tom, 独自ルールによるソート Tom, Bobby, Chris, James, Michael,
並べ替え用のメソッドは、同じ型の引数をふたつ取り、戻り値はint型で定義されます。
マイナス値を返すと第一引数の順序が前になります。
プラス値を返すと第二引数の順序が前になります。
0を返すと並べ替えを行いません。
プラス値かマイナス値か(または0か)だけに意味があり、数字の大きさは動作に影響しません。
(1を返しても100を返しても変わらない)
多次元配列の場合
Array.Sort
メソッドは多次元配列の並べ替えはできません。
Array.Reverse(要素の前後反転)
Array.Reverse
メソッドは、配列の要素の並びを反転させます。
int[] nums = new int[] { 12, 16, 0, 7, 5 };
//前後反転
Array.Reverse(nums);
foreach (var n in nums)
Console.Write("{0}, ", n);
5, 7, 0, 16, 12,
これは「降順」ではなく、現在の並び順を前後反転させるメソッドです。
Array.Sort
メソッドと同様に、範囲を指定して一部だけを反転することもできます。
int[] nums = new int[] { 12, 16, 0, 7, 5 };
//1番目の要素から3つ分を反転
Array.Reverse(nums, 1, 3);
foreach (var n in nums)
Console.Write("{0}, ", n);
12, 7, 0, 16, 5,
要素を降順で並べ替え
要素を降順に並べ替えるにはArray.Sort
メソッドを適用してからArray.Reverse
メソッドを適用します。
int[] nums = new int[] { 12, 16, 0, 7, 5 };
//昇順並べ替え
Array.Sort(nums);
//前後反転
Array.Reverse(nums);
foreach (var n in nums)
Console.Write("{0}, ", n);
16, 12, 7, 5, 0,
もしくは、並べ替え用のメソッドを自分で定義することもできます。
多次元配列の場合
Array.Reverse
メソッドは多次元配列の反転はできません。
要素の検索
Contains(要素の存在判定)
Contains
メソッドは、配列の要素に特定の値が存在するか否かを判定します。
厳密にはContains
メソッドは配列に固有のメソッドではありませんが、便利なので紹介します。
Contains
メソッドの使用にはコードの先頭にusing System.Linq;
が必要です。
using System;
using System.Linq; //←これが必要
//その他のusing~の行は変更しなくて良い
namespace ConsoleApplication1
{
//以降省略
int[] nums = new int[] { 12, 16, 0, 7, 5 };
int num1 = 12;
int num2 = 13;
Console.Write("配列nums内に「{0}」は存在しま", num1);
if (nums.Contains(num1))
Console.WriteLine("す。");
else
Console.WriteLine("せん。");
Console.Write("配列nums内に「{0}」は存在しま", num2);
if (nums.Contains(num2))
Console.WriteLine("す。");
else
Console.WriteLine("せん。");
配列nums内に「12」は存在します。 配列nums内に「13」は存在しません。
Contains
メソッドは引数に指定した値が配列内に一つでも含まれていれば真(true
)を返します。
一つも見つからなければ偽(false
)を返します。
要素が配列内のどの位置に存在するかを知りたい場合は後述のArray.IndexOf
、Array.LastIndexOf
などを使用します。
多次元配列の場合
Contains
メソッドは多次元配列には使用できません。
Array.IndexOf(要素の検索)
Array.IndexOf
メソッドは、配列から要素を検索し、その要素番号を返します。
int[] nums = new int[] { 5, 12, 16 };
//「12」を検索
int index1 = Array.IndexOf(nums, 12);
//「13」を検索
int index2 = Array.IndexOf(nums, 13);
Console.WriteLine(index1);
Console.WriteLine(index2);
1 -1
先頭の要素は「0番目」であることに注意してください。
第一引数に検索対象の配列を指定します。
第二引数は検索したい値を指定します。
要素が見つかった場合はその要素番号を返します。
見つからなかった場合は「-1」を返します。
Array.IndexOf
メソッドは、同じ要素が複数存在する場合でも最初に見つかった要素の要素の番号を返します。
二番目以降の要素を検索するには以下のように検索の開始番号を指定します。
int[] nums = new int[] { 5, 12, 16, 3, 7, 16 };
//すべての要素から「16」を検索
int index1 = Array.IndexOf(nums, 16);
//3番目の要素以降から「16」を検索
//検索対象: 3, 7, 16
int index2 = Array.IndexOf(nums, 16, 3);
//3番目の要素から2つ分まで「16」を検索
//検索対象: 3, 7
int index3 = Array.IndexOf(nums, 16, 3, 2);
Console.WriteLine(index1);
Console.WriteLine(index2);
Console.WriteLine(index3);
2 5 -1
例えば該当する要素番号をすべて取得するには以下のようにします。
int[] nums = new int[] { 0, 12, 16, 0, 7, 0, 3 };
int index = 0;
//「0」が出現する位置をすべて表示
//1、Array.IndexOfメソッドを実行
//2、実行結果を変数indexに代入
//3、代入した値が0以上かを判定
while ((index = Array.IndexOf(nums, 0, index)) >= 0)
{
Console.WriteLine(index);
++index; //見つかった番号の次から検索
}
0 3 5
単純に「要素が存在するか否か」だけで良い場合はContains
メソッドを使用したほうが簡単です。
多次元配列の場合
Array.IndexOf
メソッドは多次元配列には使用できません。
Array.LastIndexOf(要素の後方検索)
Array.IndexOf
メソッドは配列の先頭から要素を検索しますが、後方から検索したい場合はArray.LastIndexOf
メソッドを使用します。
int[] nums = new int[] { 0, 12, 16, 0, 7, 0, 3 };
//前方検索
int index = Array.IndexOf(nums, 0);
//後方検索
int lastIndex = Array.LastIndexOf(nums, 0);
Console.WriteLine(index);
Console.WriteLine(lastIndex);
0 5
検索が後方から行われる以外はArray.IndexOf
メソッドと同じです。
戻り値は後ろから数えた番号ではなく要素番号なので注意してください。
多次元配列の場合
Array.LastIndexOf
メソッドは多次元配列には使用できません。
Array.BinarySearch(要素の二分検索)
要素の検索にはArray.BinarySearch
メソッドを使用する方法もあります。
Array.IndexOf
メソッドは要素を先頭から順番に検索していきますが、Array.BinarySearch
メソッドは二分検索という方法で検索を行います。
この検索方法はArray.IndexOf
メソッドよりも高速な検索が可能ですが、要素が昇順で並べ替えられていなければなりません。
昇順で並べられていない場合は正しい結果が得られません。
そのため、要素が昇順で並んでいることが確定している場合以外は事前にArray.Sort
メソッドを適用しておく必要があります。
要素が見つかった場合は戻り値は要素番号です。
見つからなかった場合は負数(マイナス値)を返します。
int[] nums = new int[] { 12, 16, 0, 7, 5 };
//昇順で並べ替え
Array.Sort(nums);
//「12」を検索
int index = Array.BinarySearch(nums, 12);
foreach (var n in nums)
Console.Write("{0}, ", n);
Console.WriteLine("\n「12」は{0}番目", index);
0, 5, 7, 12, 16, 「12」は3番目
検索を開始する要素番号と、検索する要素の数を指定して検索することもできます。
char[] chars = new char[] { 'a', 'b', 'c', 'd', 'e' };
//1番目から2個分の範囲で'c'を検索
Console.WriteLine(Array.BinarySearch(chars, 1, 2, 'c'));
//3番目から2個分の範囲で'c'を検索
Console.WriteLine(Array.BinarySearch(chars, 3, 2, 'c'));
2 -4
Array.BinarySearch
は検索する要素にIComparable
インターフェイスが実装されている必要があります。
詳しい説明は省きますが、自作クラスを検索する場合には注意が必要です。
int型などの組み込み型のデータ型の場合は問題ありません。
(→オブジェクトの比較)
多次元配列の場合
Array.BinarySearch
メソッドは多次元配列には使用できません。
Array.Find(条件に合致する要素の取得)
Array.Find
メソッドは、任意の条件に合致する要素を取得します。
//引数が10以上なら真を返す関数
static bool IsGTE10(int n)
{
return n >= 10;
}
static void Main(string[] args)
{
int[] nums1 = new int[] { 3, 16, 0, 7, 5 };
int[] nums2 = new int[] { 1, 2, 3 };
int index1 = Array.Find(nums1, IsGTE10);
int index2 = Array.Find(nums2, IsGTE10);
Console.WriteLine(index1);
Console.WriteLine(index2);
Console.ReadLine();
}
16 0
Array.Find
メソッドの使用には、配列の要素の型を引数にひとつ取り、bool型を返すメソッドが必要です。
今回は、引数の数値が10以上なら真を返すIsGTE10
というメソッドを定義しています。
(Is Greater Than or Equal to 10)
このメソッドをArray.Find
メソッドの引数に指定します。
このとき、引数のメソッドには関数呼び出し演算子()
は指定せず、メソッド名のみを指定することに注意して下さい。
戻り値は最初に条件に合致する要素です。
合致する要素がない場合は既定値を返します。
(int型の場合は「0」)
Array.IndexOf
メソッドとは違い要素番号ではないことに注意してください。
ラムダ式による記述も参照してください。
Array.Findメソッドの仲間
条件判定メソッドを使用して要素を検索するメソッドは他にも存在します。
使い方は同じなので、以下にまとめて示します。
詳細はコード中のコメントを参照してください。
static bool IsGTE10(int n)
{
return n >= 10;
}
static void Main(string[] args)
{
int[] nums = new int[] { 3, 16, 0, 12, 15, 8 };
//最初に見つかった要素を返す
int find =
Array.Find(nums, IsGTE10);
//最後に見つかった要素を返す
int findLast =
Array.FindLast(nums, IsGTE10);
//最初に見つかった要素の要素番号を返す
int findIndex =
Array.FindIndex(nums, IsGTE10);
//最後に見つかった要素の要素番号を返す
int findLastIndex =
Array.FindLastIndex(nums, IsGTE10);
//条件に合致するすべての要素を配列で返す
int[] findAll =
Array.FindAll(nums, IsGTE10);
//条件に合致する要素が存在するかを真偽値で返す
bool exists =
Array.Exists(nums, IsGTE10);
//すべての要素が条件に合致するかを真偽値で返す
bool trueForAll =
Array.TrueForAll(nums, IsGTE10);
Console.Write("検索対象の配列: ");
foreach (var n in nums)
Console.Write("{0}, ", n);
Console.WriteLine("\n検索条件: n >= 10\n");
Console.WriteLine("Find: {0}", find);
Console.WriteLine("FindLast: {0}", findLast);
Console.WriteLine("FindIndex: {0}", findIndex);
Console.WriteLine("FindLastIndex: {0}", findLastIndex);
Console.Write("FindAll: ");
foreach (var n in findAll)
Console.Write("{0}, ", n);
Console.WriteLine();
Console.WriteLine("Exists: {0}", exists);
Console.WriteLine("TrueForAll: {0}", trueForAll);
Console.ReadLine();
}
検索対象の配列: 3, 16, 0, 2, 15, 8, 検索条件: x >= 10 Find: 16 FindLast: 15 FindIndex: 1 FindLastIndex: 4 FindAll: 16, 12, 15, Exists: True TrueForAll: False
Array.FindIndex
メソッドとArray.FindLastIndex
メソッドは、要素が見つからなかった場合は「-1」を返します。
これらのメソッドにはオーバーロードが存在します。
static bool IsGTE10(int n)
{
return n >= 10;
}
static void Main(string[] args)
{
int[] nums = new int[] { 0, 1, 12, 3, 4, 15 };
//3番目の要素から検索開始
int findIndex1 = Array.FindIndex(nums, 3, IsGTE10);
//3番目の要素から2つ分を検索
int findIndex2 = Array.FindIndex(nums, 3, 2, IsGTE10);
//4番目の要素から検索開始
int findLastIndex1 = Array.FindLastIndex(nums, 4, IsGTE10);
//4番目の要素から2つ分を検索
int findLastIndex2 = Array.FindLastIndex(nums, 4, 2, IsGTE10);
Console.WriteLine(findIndex1);
Console.WriteLine(findIndex2);
Console.WriteLine(findLastIndex1);
Console.WriteLine(findLastIndex2);
Console.ReadLine();
}
5 -1 2 -1
Array.FindLastIndex
メソッドは後方から検索する以外はArray.FindIndex
メソッドと同じです。
検索開始番号の指定や戻り値などはすべて配列の要素番号です。
後方から検索すると言ってもこれらも前後逆になるわけではないので注意してください。