急いで学ぶGo lang#7 range・Array・slice・map

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Goでよく使用するデータ型

一般的なプログラミング言語では配列・リスト・マップ等よく使用されるデータ型がありますが、
Goでもそういったデータ型はよく使用されます。
今回はGoでよく使用するデータ型、「Array(配列)」「Slice」「map」と、それらをイテレートするrangeについて紹介します。

動作環境

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.9.4
  • Go : 1.4.1
  • IDE : IntelliJ IDEA 14 CE

ここを参考に、GOPATHとGOROOTは設定しておいてください。

いろいろなデータ型を使ってみる

range

これは後述するスライス(またはマップなど)の要素数ループを繰り返します。
下記サンプルでは、intの配列をforループでまわしています。

//int配列
nums := []int{2, 3, 4}
//forループ
for i := 0; i < len(nums); i++ {
  fmt.Print(fmt.Sprintf("index:%d,value:%d\n", i, nums[i]))
}
[/go]
</p>

<p>
rangeを使うように修正すると、下のようになります。</br>	
下記例では変数iにループのインデックス、変数vにrangeで指定したnums要素が代入されます。</br>

//rangeを使う
for i, v := range nums {
  fmt.Print(fmt.Sprintf("index:%d,value:%d\n", i, v))
}

Array(配列)

同じ型の要素を任意の数格納するための型です。下記構文で宣言することができます。
var arr [n]type
nは配列の長さを表し、typeは要素の型を表します。長さも配列の一部となるので、[5]intと[10]intは異なる型と判断されます。
また、配列は途中で長さを変えることができません。

var a [5]int
fmt.Println("a:", a) //a: [0 0 0 0 0]
a[4] = 100
fmt.Println("a:", a) //a: [0 0 0 0 100]
fmt.Println("a[4]:", a[4]) // a[4]: 100

b := [5]int{1, 2, 3, 4, 5}
fmt.Println("b[:]:", b[:])     // [1 2 3 4 5]
fmt.Println("b[1:4]:", b[1:4]) // [2 3 4]
fmt.Println("b[:4]:", b[:4])   // [1 2 3 4]
fmt.Println("b[1:]:", b[1:])   // [2 3 4 5]

配列にアクセスするためには、[]をつかって任意の要素にアクセス可能です。
また、上記例のように添字の「:」の位置により、範囲を指定した配列アクセスが可能です。
配列同士の代入はポインタではなく値渡しとなる点に注意してください。引数として配列が渡された場合、その配列はコピーされます。

Slice(スライス)

配列よりも柔軟でサイズ変更も可能なものがSliceです。
Sliceはサイズを明示的に持ちません。Sliceでは代入の際にポインタがわたされるので、データが共有されます。
GoにおいてSliceは通常の配列より柔軟で効率的なためよく使用されるみたいです。
しかし、通常の配列のようには細かいメモリ割り当ての制御はできないため、そういったケースでは配列と使い分けが必要です。

Sliceは次のような形式で宣言します。

//宣言
var slice1 []int
//リテラル
slice2 := []int{"a","b","c"}

Sliceから再度Sliceを作成することができます。aとbは同じメモリ領域を指します。

//sliceからslice作成
a := [5]int{1, 2, 3, 4, 5}
b := a[2:4]
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(b) //[3 4]

配列からSliceを作成することも可能です。次の場合もarrayとsliceは同じメモリ領域を指します。

//配列からslice作成
array := []string{"a", "b", "c"}
slice := array[:]
fmt.Println(array) //[a b c]
fmt.Println(slice) //[a b c]

Sliceの場合、make関数をつかって作成することができます。

//make(型,長さ,容量)
var slice []int = make([]int, 3, 3)
fmt.Println(slice) //[0 0 0]

make関数はオブジェクトを初期化してくれます。
makeの題3引数の容量は省略可能で、その場合第2引数の長さと容量はおなじになります。
len関数を使えば長さ、cap関数を使えば容量を取得することができます。

var slice []int = make([]int, 3, 3)
len(slice) //3
cap(slice) //3

meke時にlenよりもcapのほうを大きくした場合、capのサイズまで拡張が可能です。
もしそれ以上にサイズを大きくしたい場合、容量を増やします。

//ループで1つずつ代入する方法
s := make([]byte, 5)
t := make([]byte, len(s), (cap(s)+1)*2)
for i := range s { 
  t[i] = s[i]
}
//copy関数を使う方法
//もとのSliceのsrcをdstにコピーしてコピーした要素の個数を返す
s := make([]byte, 5)
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)

append関数はSliceに対して1つ/複数の要素を追加します。その後Sliceと同じ型のSliceを返します。
下記サンプルのように、append関数を使えばSliceのサイズを自動的に拡張してくれます。

//自動的に拡張
var slice []int = make([]int, 3)
fmt.Println(slice) //[0 0 0]
slice = append(slice, 10, 20, 30, 40, 50)
fmt.Println(slice) //[0 0 0 10 20 30 40 50]

任意の要素を削除する関数はないので、その場合には自分でSliceを作り直す必要があります。

map

連想配列。すなわちマップです。キーを使ってデータへアクセスします。
mapの場合もmakeを使用して初期化します。

var map1 map[string]int = make(map[string]int)
map1["x"] = 10
map1["y"] = 20
fmt.Println(map1["x"]) //10

map2 := map[string]int{"x": 10, "y": 20}
fmt.Println(map2) //map[x:10 y:20]

要素を削除するにはdeleteを使用します。

map1 := map[string]int{"x": 10, "y": 20}
delete(map1, "x")
fmt.Println(map1) //map[y:20]

mapは2つの戻り値があり、2つ目の戻り値では、keyが存在しない場合false、存在すればtrueになります。

map1 := map[string]int{"x": 10, "y": 20}
value, ok := map1["x"]
fmt.Println(ok) //true

まとめ

今回はGoでよく使用するrange、配列、slice、mapをつかってみました。
これらは非常によく使用するのでしっかり覚えておく必要がありそうです。

参考サイトなど

  • paraなんとか

    配列とスライスが逆になっている箇所がありませんか?
    私が気づいたのは
    //int配列
    //sliceからslice作成
    //配列からslice作成

    の3箇所。

    nums := [3]int{2, 3, 4} // 要素数を書くと配列
    nums := […]int{2, 3, 4} // 要素数を…と書くと配列(コンパイラに数えさせる)
    nums := []int{2, 3, 4} // 要素数を書かないとスライス

    ですよね?