配列を宣言する
配列の型は [N]T と書きます。長さの後に要素の型が続きます。長さはコンパイル時に固定され、型の一部になります。
出力:
[90 0 0 0 75] 5
Tue
[5]int [2 3 5 7 11]
["" "b" "" "d" ""]
配列は常にすべて初期化されています。設定しなかった要素は、要素の型のゼロ値(0、""、false、nil)を保持します。
インデックスと範囲
インデックスは 0 から len(a)-1 までです。範囲外の定数インデックスはコンパイルエラーになります。範囲外の変数インデックスは実行時にpanicします。
var a [3]int
a[5] = 1 // compile error: invalid argument: index 5 out of bounds [0:3]
i := 5
a[i] = 1 // panic: runtime error: index out of range [5] with length 3
走査する
for i, v := range a は各要素を順に訪れます。v は要素のコピーなので、v に代入しても配列は変わりません。代わりにインデックスを通して書き込みます。
配列に特有の細かな点がもう1つあります。配列の値に対する range は、最初に配列のコピーを評価します。本体が後ろの要素を変更しても、v は古い値のままです。&a か a[:] に対してrangeすればコピーを避けられます。
配列は値
これがC、Java、JavaScriptの配列との主な違いです。配列を代入するとすべての要素がコピーされます。配列を関数に渡してもコピーされます。
出力:
[1 2 3] [99 2 3]
[1 2 3]
[0 2 3]
小さな配列ならコピーは安価ですが、大きな配列では高くつきます。[1_000_000]int を値で渡すと8メガバイトがコピーされます。配列が大きいときや関数が変更する必要があるときは、ポインタ(*[N]T)かスライス(a[:])を渡します。配列へのポインタを通したインデックスには明示的な * は不要で、*[3]int に対して a[0] が使えます。
配列の比較とマップのキー
比較可能な要素の配列は == と != をサポートします。2つの配列は、すべての要素が等しいときに等しくなります。そのため、スライスと違ってマップのキーとしても使えます。
長さの違う配列は別の型なので、そもそも比較できません。[3]int{} == [4]int{} はコンパイルできません(mismatched types [3]int and [4]int)。
多次元配列
配列の配列は固定のグリッドになります。グリッド全体が1つの連続したメモリブロックです。
row[:] は [3]rune の行を []rune のスライスに変え、それを string() が受け付けます。
配列とスライスの比較
配列 [N]T | スライス []T | |
|---|---|---|
| 長さ | 固定、型の一部 | append で変わる |
| 代入と受け渡し | すべての要素をコピーする | 小さなヘッダーをコピーし、要素を共有する |
| ゼロ値 | N個のゼロ値の要素 | nil、長さ0 |
== | 使える、要素ごとに比較 | nil との比較のみ |
| マップのキー | なれる | なれない |
| 典型的な用途 | 固定サイズのデータ:ハッシュ、座標、バッファ | ほぼそれ以外すべて |
実際に配列を目にするのは、いくつかの場所に限られます。sha256.Sum256 が返す [32]byte、サイズが変わらないルックアップテーブル、そしてスライスの元の配列です。a[:] や a[1:3] で配列をスライスすると、メモリを共有するスライスが作られます。その共有が何を意味するかはスライスのページで扱っています。
配列とスライスの変換
スライスを配列(Go 1.20)や配列へのポインタ(Go 1.17)に変換すると、スライスが配列の長さより短い場合はpanicします:panic: runtime error: cannot convert slice with length 2 to array or pointer to array with length 3。
よくある間違い
- 関数が配列の引数を変更すると期待する。 関数はコピーを受け取ります。
*[N]Tかスライスを渡します。 - スライスが必要な場所で配列を使う。
[]int{1, 2}はスライス、[2]int{1, 2}は配列です。[]intを受け取る関数は[2]intを受け付けないので、a[:]を渡します。 - rangeループでの大きな配列。
for _, v := range bigArrayは最初に配列全体をコピーします。&bigArrayかスライスに対してrangeします。
よくある質問
Goで配列はどう宣言しますか?
要素の型の前に、角かっこで長さを書きます:var a [5]int はゼロを5つ作ります。値を指定するなら a := [3]string{"x", "y", "z"} です。[...] を使うとコンパイラが数えてくれます:a := [...]int{1, 2, 3} の型は [3]int です。
Goの配列とスライスの違いは何ですか?
配列は型の一部である固定長を持ち([3]int と [4]int は別の型です)、代入したり渡したりするとコピーされます。スライス([]int)は元の配列に対するビューで、長さは append で変わり、スライスをコピーしても要素ではなくビューだけがコピーされます。ほとんどのGoのコードはスライスを使います。
Goで配列の長さを取得するには?
組み込みの len(a) を使います。配列の場合はコンパイル時の定数なので、定数式の中でも使えます。cap(a) も同じ値を返します。
Goで配列を比較できますか?
要素の型が比較可能なら、== と != で比較できます。2つの配列は、すべての要素が等しいときに等しくなります。配列はマップのキーにもなれます。スライスは(nil との比較を除いて)== で比較できないので、slices.Equal を使います。