■配列
他の言語と同様、C#でも配列が使えます。
配列とは、同じ型の変数を複数個集め、まとめたものに名前をつけたものです。
例えば、日記をつけるとします。
この記事を書いている8月は1日〜31日まであるので
1日の記事を31個あつめて、まとめて8月の日記
つまり配列として定義すると便利です。
C#の配列はC,C++のそれとは書式が異なるので注意が必要です。
具体的な書式や例について順に説明します。
1次元配列は最も単純な配列で
プログラミングにおいて最も利用されます。
例として、複数のint型変数を扱う配列を宣言してみます。
【例】
int[] data;
intの後ろの[]で、int型の配列を宣言しようとしていることがわかります。
配列の中に何個の変数が格納されるのか、ここではまだ未定です。
最後に変数にdataと名前が付けられました。
このままでは、複数個のint型を扱える
dataという名の配列参照変数が定義されただけに過ぎません。
(値型ではなく参照型である点に注意してください)
dataは参照型の変数で、何かを参照するまで
それ自身が値を持つことはできません。
続いて、実際に10個のint型変数の集まりを宣言し、
この配列参照変数と結びつけてみます。
data = new int[10];
以上で、dataというint型変数10個の集まりを扱う
配列が宣言されました。
なお、配列名の宣言と、変数の集まりの結びつけを
1行にまとめて記述することも可能です。
【例】
int[] data = new int[10];
【書式】
型名 [] 配列名 = new 型[サイズ];
それでは、配列を使ったサンプルプログラムを書いてみます。
先ほど同様、int型を10個扱える配列を作成し
そこに1〜10の整数を1つずつ設定します。
最後に、それら10個の値を合計した値を出力します。
【補足】
C#の配列の添え字はC,C++同様、0からはじまるので
int[10]
と宣言すると、[0]〜[9]の10個の変数が宣言されます。
添え字の範囲に注意してください。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
// 配列の宣言
int[] data = new int[10];
// data[0]〜data[9]に値を設定
for ( int i = 0; i < 10; i ++ )
data[i] = i;
// data[0]〜data[9]の合計値を求める
int total = 0;
for ( int i = 0; i < 10; i ++ )
total += data[i] = i;
// 合計値を出力
Console.WriteLine( "合計:{0}", total );
}
}
}
【出力結果】
合計:45
1次元配列の添え字は1つでしたが
多次元配列では次元数分、添え字の数が増えます。
例えば、3個×4個の
2次元配列の宣言と初期化は次のようになります。
int[,] data = new int[3,4];
図で説明すると、次のようなイメージになります。
将棋やオセロの盤を想像してください。
1 2 3 4 ←2番目の添え字
┌───┬───┬───┬───┐
0│[0,0] │[0,1] │[0,2] │[0,3] │
├───┼───┼───┼───┤
1│[1,0] │[1,1] │[1,2] │[1,3] │
├───┼───┼───┼───┤
2│[2,0] │[2,1] │[2,2] │[2,3] │
└───┴───┴───┴───┘
↑1番目の添え字
なお、C,C++の場合は、添え字の数だけ[]が増えますが
C#ではカンマの数が増えます。
例として
上記イメージの縦軸と横軸の添え字の
加算結果を各配列の箱の中に設定するサンプルコードを書いてみます。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
// 3*4個の2次元配列を宣言
int[,] data = new int[3,4];
// 各箱にX軸とY軸の加算結果を設定
for ( int y = 0; y < 3; y ++ )
for ( int x = 0; x < 4; x ++ )
data[y,x] = y + x;
// 箱の中身を表示
for ( int y = 0; y < 3; y ++ )
{
for ( int x = 0; x < 4; x ++ )
Console.Write( "[{0},{1}]={2} ", y, x, data[y,x] );
Console.WriteLine( "" );
}
}
}
}
【出力結果】
[0,0]=0 [0,1]=1 [0,2]=2 [0,3]=3
[1,0]=1 [1,1]=2 [1,2]=3 [1,3]=4
[2,0]=2 [2,1]=3 [2,2]=4 [2,3]=5
3次元以上のさらに多くの配列を宣言する場合も同様の書式で
カンマの数を増やします。
3次元配列の場合は次のようになります。
【書式】
int[,,] data = new int[3,3,3];
C#にはジャグ配列という、ちょっと変わった配列が用意されています。
一言で言うと、配列の配列です。
いくつかの配列が集まった、配列です。
ややこしいですが、実例を見てイメージしてください。
まず例として
3つの配列を格納する配列(ジャグ配列)を宣言します。
int[][] data = new int[3][];
このままではまだ空っぽのジャグ配列なので
3つの配列を順に格納してみます。
data[0] = new int[2]; // ジャグ配列の0番目に1次元配列int[2]を格納
data[1] = new int[3]; // ジャグ配列の1番目に1次元配列int[3]を格納
data[2] = new int[4]; // ジャグ配列の2番目に1次元配列int[4]を格納
このように、配列を配列に格納しています。
図で説明すると、以下のようにデコボコした形になります。
これら3つの配列をまとめてジャグ配列
┌──────────────────────────────────┐
│ ┌─────┬─────┐ │
│ 0番目の配列 │data[0][0]│data[0][1]│ │
│ └─────┴─────┘ │
│ ┌─────┬─────┬─────┐ │
│ 1番目の配列 │data[1][0]│data[1][1]│data[1][2]│ │
│ └─────┴─────┴─────┘ │
│ ┌─────┬─────┐─────┬─────┐ │
│ 2番目の配列 │data[2][0]│data[2][1]│data[2][2]│data[2][3]│ │
│ └─────┴─────┘─────┴─────┘ │
└──────────────────────────────────┘
ジャグ配列内の各要素へアクセスするには
data[0][0]
のようにします。
それでは具体的に、
1年分(365日)の降水量を格納するジャグ配列を定義してみます。
2次元配列でも似たような配列を定義可能ですが
ジャグ配列であればこのような場合
月ごとに必要な日数分のエリアだけ配列を定義できるので
無駄がありません。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
// 12ヶ月分のジャグ配列
int[][] data = new int[12][];
data[0] = new int[31]; // 1月
data[1] = new int[28]; // 2月
data[2] = new int[31]; // 3月
data[3] = new int[30]; // 4月
data[4] = new int[31]; // 5月
data[5] = new int[30]; // 6月
data[6] = new int[31]; // 7月
data[7] = new int[31]; // 8月
data[8] = new int[30]; // 9月
data[9] = new int[31]; // 10月
data[10] = new int[30]; // 11月
data[11] = new int[31]; // 12月
// 中略
data[9][23] = 10;
// 中略
int month = 10;
int day = 24;
Console.WriteLine( "{0}月{1}日の降水量は{2}mmです。",
month,
day,
data[month - 1][day - 1] );
}
}
}
【実行結果】
10月24日の降水量は10mmです。
配列を宣言すると同時に、初期値を設定する書き方があります。
各次元ごとに説明します。
【1次元配列の場合】
int[] data = { 1, 2, 3, 4, 5 };
結果、
data[0] = 1
data[1] = 2
data[2] = 3
data[3] = 4
data[4] = 5
のように配列の各要素に初期値が設定されます。
newが無い点に注意してください。
宣言と同時に初期値を設定する場合、newは不要です。
仮に
int[] data = new int[5] { 1, 2, 3, 4, 5 };
としても文法的には正しく、また動作にも問題ありませんが
どちらも同じ効果ですので
前者の短いほうを使った方がコードは見やすいと思います。
なお、次のコードのように
先に配列を宣言し、後から初期値を設定したい場合は
newが必要になるので注意してください。
int [] data;
data = new int[5] { 1, 2, 3, 4, 5 };
【多次元配列の場合】
int [,] data = {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 } // 最後なのでカンマ不要
};
上記は2次元配列の例ですが、次元の数だけ{}がネストします。
カンマの位置や{}の対応に注意してください。
結果、
data[0,0] = 1
data[0,1] = 2
data[0,2] = 3
data[1,0] = 4
data[1,1] = 5
data[1,2] = 6
data[2,0] = 7
data[2,1] = 8
data[2,2] = 9
のように配列の各要素に初期値が設定されます。
【ジャグ配列の場合】
int[][] data = {
new int[2] { 1, 2 },
new int[3] { 3, 4, 5 },
new int[4] { 6, 7, 8, 9 } // 最後なのでカンマ不要
};
ジャグ配列は配列の配列なので
配列の初期値を設定する文を、複数定義します。
結果、
data[0][0] = 1
data[0][1] = 2
data[1][0] = 3
data[1][1] = 4
data[1][2] = 5
data[2][0] = 6
data[2][1] = 7
data[2][2] = 8
data[2][3] = 9
のように配列の各要素に初期値が設定されます。
C#では、配列はオブジェクトなので
それ自身がメソッドやプロパティを持ちます。
その中のLengthプロパティで
配列の要素数を取得することが可能です。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
// 1次元配列
int[] data1 = { 1, 2, 3 };
Console.WriteLine( "1次元配列の要素数:{0}", data1.Length );
// 2次元配列
int[,] data2 = {
{ 1, 2 },
{ 3, 4 }
};
Console.WriteLine( "2次元配列の要素数:{0}", data2.Length );
// ジャグ配列
int[][] dataj = {
new int[2] { 1, 2 },
new int[3] { 3, 4, 5 },
new int[4] { 6, 7, 8, 9 }
};
Console.WriteLine( "ジャグ配列の要素数:{0}", dataj.Length );
}
}
}
【実行結果】
1次元配列の要素数:3
2次元配列の要素数:4
ジャグ配列の要素数:3
【補足】
注目すべきはジャグ配列のLengthプロパティの値です。
この場合、ジャグ配列内の全要素数ではなく
ジャグ配列に格納されている配列の数が返されます。
ジャグ配列内の全要素の数が欲しい場合は次のようにします。
// ジャグ配列
int[][] dataj = {
new int[2] { 1, 2 },
new int[3] { 3, 4, 5 },
new int[4] { 6, 7, 8, 9 }
};
// 全要素数取得
int total = 0;
for ( int i = 0; i < dataj.Length; i ++ )
total += dataj[i].Length;
Console.WriteLine( "ジャグ配列の要素数:{0}", total );
結果は全要素数の9です。
【おまけ】
前述の1年365日の降水量を表すジャグ配列の例の続編として
1年間の降水量の合計を求めるコードを書いてみます。
なお、あまりにデータ件数が多いので
実際に降水量を設定するコードは省略します。
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
// 12ヶ月分のジャグ配列
int[][] data = new int[12][];
data[0] = new int[31]; // 1月
data[1] = new int[28]; // 2月
data[2] = new int[31]; // 3月
data[3] = new int[30]; // 4月
data[4] = new int[31]; // 5月
data[5] = new int[30]; // 6月
data[6] = new int[31]; // 7月
data[7] = new int[31]; // 8月
data[8] = new int[30]; // 9月
data[9] = new int[31]; // 10月
data[10] = new int[30]; // 11月
data[11] = new int[31]; // 12月
// データ設定は省略します
// 1年間の降水量を求め、出力
int total = 0;
for ( int i = 0; i < data.Length; i ++ )
for ( int j = 0; j < data[i].Length; j ++ )
total += data[i][j];
Console.WriteLine( "年間降水量:{0}mm", total );
}
}
}
foreachを使うと
配列のように、いくつかの要素が集まったコレクションに
順番にアクセスすることができます。
本節ではforeachを配列に対して使う例を示しますが
foreachの対象となるコレクションは配列に限定されません。
(配列以外のコレクションに対するforeach例は別途記述)
ではさっそく、1次元配列の全要素へ順にアクセスし、
その内容をすべて出力するサンプルを書いてみます。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
int[] array = { 10, 20, 30, 40, 50 };
foreach( int data in array )
Console.WriteLine( data );
}
}
}
【実行結果】
10
20
30
40
50
実行結果を見てわかるように
配列の添え字の小さいもの〜大きいものまで
全要素へ順にアクセスし、
ループする都度ひとつの要素の値がdataに格納され、
それを出力しています。
【書式】
コレクションに対し、単一文を実行する場合
foreach( 型名 変数名 in コレクション ) 文;
コレクションに対し、複数文を実行する場合
foreach( 型名 変数名 in コレクション )
{
文1;
文2;
}
【補足1】
上記Sampleコード中のdata変数のことを
特に反復変数と呼びますが
この変数は、要素のコピーを格納するものなので
dataに値を変更しても、コピー元のarray配列には結果が反映されません。
【補足2】
コレクションの要素の型と、反復変数の型は
一致していることが望ましいのですが
異なる型でもキャスト可能であれば、一応動きます。
異なる型を用いるような理由が生じることはあまり無いと思いますが
例えばint配列をbyte型の反復変数へ代入すると・・・
意図しない結果が待っています。
【補足3】
あまり必要性は無いかもしれませんが
foreachループ中で、現在何番目の要素を使っているのかを知るには
自分でカウンターのようなものを用意して
インクリメントしていくしかありません。
また、foreachは多次元配列でも利用可能です。
以下に2次元配列の全要素を出力するサンプルを書いてみます。
配列の宣言と初期化部分以外のコードは
先ほどのサンプルコードとまったく同じです。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
int[,] array = {
{ 1, 2, 3 },
{ 4, 5, 6 }
};
foreach( int data in array )
Console.WriteLine( data );
}
}
}
【実行結果】
1
2
3
4
5
6
System.ArrayクラスのSortメソッドを使って
配列の要素を並べ替えることができます。
例として、1次元配列の整数を昇順に並べ替えるコードを書いてみます。
【Sampleコード】
using System;
namespace SampleArray
{
class Class1
{
static void Main(string[] args)
{
int[] array = { 41, 23, 30, 8, 13 };
Array.Sort( array );
foreach( int data in array )
Console.WriteLine( data );
}
}
}
【動作結果】
8
13
23
30
41
【補足】
この方法でSortできるのは1次元Arrayオブジェクトのみです。
従って、多次元配列をSortすることはできません。
コンパイルは通りますが、実行時にSortする段階でエラーとなります。
整数同様、文字列も昇順にSortすることができます。
上記サンプルのint型をstring型に変更して実験してみてください。
なお、上記サンプルとは逆に
整数を降順に並び替えたり
ユーザ定義のルールに従った並び替えも可能です。
詳しくはMSDN等を参照してください。
(並び替える要素の範囲も指定可能です)
ここでは詳しく説明しませんが
ユーザ定義Classのルールに従って
先ほどのサンプルを変更して
整数を降順に並び替えるコードを示します。
【Samleコード】
using System;
using System.Collections; // IComparer用
public class SamplesArray
{
public class CCompare : IComparer
{
int IComparer.Compare( Object a, Object b )
{
return( (new CaseInsensitiveComparer()).Compare( b, a ) );
}
}
public static void Main()
{
int[] array = { 41, 23, 30, 8, 13 };
IComparer comparer = new CCompare();
Array.Sort( array, comparer );
foreach( int data in array )
Console.WriteLine( data );
}
}
【動作結果】
41
30
23
13
8
Copyright © 2008.07 - shougo suzaki
|