Go言語の配列・スライスの基本

今回は Arrays(配列) と slices(スライス) に焦点を当てて学習しました。

Arrays(配列)

配列とは、同じ型を持つ値(要素)を並べたものです。
Goの Arrays(配列) は固定長の的な配列なので最初に宣言した配列のサイズを変えることはできません。

配列の宣言方法

Goでは配列を以下の様に宣言します。

① var 変数名 [長さ]型
② var 変数名 [長さ]型 = [大きさ]型{初期値1, 初期値n} 
③ 変数名 := [...]型{初期値1, 初期値n}

①の方法で配列を宣言した例

func main(){
   var arr[2] string 
       arr[0] = "Golange"
       arr[1] = "Java"
       fmt.Println(arr[0], arr[1]) //=> Golange Java
       fmt.Println(arr) //=> [Golange Java]
}

②の方法で配列を宣言した例

func main(){
   var arr[2] string = [2]string {"Golang", "Java"}
       fmt.Println(arr[0], arr[1]) //=> Golange Java
       fmt.Println(arr) //=> [Golange Java]
}

③の方法で配列を宣言した例
この宣言方法では要素数は省略が可能です。
しかし、後ろに書いた要素の数を自動でカウントしてくれているだけで、要素数を明記した場合と実態に違いはありません。

func main(){
     arr := [...] string{"Golang", "Java"}
     fmt.Println(arr[0], arr[1]) //=> Golange Java
     fmt.Println(arr) //=> [Golange Java]
}

Slices(スライス)

Goの Arrays(配列)は固定長の様な配列である一方、Goの Slices(スライス) は可変長の配列の様な動きをするのでより柔軟にデータ(要素)を格納することが可能です。

スライスの宣言方法

Goではスライスを以下の様に宣言します。
配列の宣言と違って、スライスは [ ] の中に大きさを指定しません。
また、スライスは配列への「参照型」で値を持つため、配列から部分列を取り出す事 (スライス操作) でも作成する事が可能です。

① var 変数名 []型
② var 変数名 []型 = []型{初期値1, ..., 初期値n} 
③ 変数名 := 配列[start:end] 
//配列(またはスライス)のstartから(end - 1)を取り出す事でスライスを作成する。 

①の方法で宣言した例

func main(){
     var slice []string
     fmt.Println(slice) //=> []
}

②の方法で宣言した例

func main(){
     slice := [] string{"Golang", "Java"}
     fmt.Println(slice) //=> [Golange Java]
}

③の方法で宣言した例

func main(){
     arr := [...] string{"Golang", "Java"}
     slice := arr[0:2]
     fmt.Println(slice) //=> [Golange Java]
}

スライス操作には以下の様なものがあります。
上記の例では Slice[start:end] の例を使用しています。

操作意味
Slice[start:end]start から end – 1 まで
Slice[start:]start から最後尾まで
Slice[:end]先頭から end – 1 まで
Slice[:]先頭から最後尾まで

スライス操作を行う場合は生成元の配列(またはスライス)と要素を共有します。
つまり、スライスの要素を変更すると、その元となる配列の対応する要素が変更されます。

func main(){
     arr := [...] string{"Golang", "Java"}
     slice := arr[0:2] //スライスの作成

     slice[0] = "Ruby" //slice[0]の要素を変更
     fmt.Println(arr) //=> [Ruby Java] //arrの要素も変更されている事を確認
}

要素の追加

Goでは配列は宣言時に要素の数が固定されるので要素の追加はできません。
スライスに要素を追加したい場合は組み込み関数 append() を使用する事で簡単に行えます。
(append は新しいスライスを返すことに注意してください。)

以下の様に書く事で元の配列(スライス)の末尾に指定した要素を追加した値を返します。

newSlice = append(slice, 追加要素)
func main(){
     slice := [] string{"Golang", "Java"}
     newSlice := append(slice, "Ruby") //sliceに"Ruby"を追加

     fmt.Println(newSlice) //=> [Golang Java Ruby]
     fmt.Println(slice) //=> [Golang Java] //元の値は変わっていない。
}

スライスの長さ(length)と容量(capacity)

スライスは 長さ( length ) と 容量( capacity ) の両方を持っています。

  • 長さ(length) は、それに含まれる要素の数です。
  • 容量(capacity) は、スライスの最初の要素から数えて、元となる配列の要素数です。

スライスの長さと容量は len() と cap() という式を使用して得ることができます。

func main(){
    arr := [...] string{"Golang", "Java"}
    slice := arr[0:1]

    fmt.Println(slice) //=> Golang 
    fmt.Println(len(slice)) //=> 1 //sliceの要素数は1つなので1を返す。
    fmt.Println(cap(slice)) //=> 2 //sliceの生成元の要素数は2つなので2を返す。
}

スライスの代入

スライスの型が一致している場合、スライスを代入することができます。

func main(){
     slice := [] string{"Golang", "Java"}
 var slice2 []string //sliceと同型slice2を作成

     slice2 = slice //sliceをslice2に代入

     fmt.Println(slice2) //=> [Golange Java]
}

slice からも slice2 の配列本体にアクセスすることができます。
また、slice[0] の値を書き換えると、slice2 の値も変化します。

func main(){
     slice := [] string{"Golang", "Java"}
 var slice2 []string //sliceと同型slice2を作成

     slice2 = slice //sliceをslice2に代入
     slice2[0] = "Ruby" //slice2[0]の値を変更

     fmt.Println(slice2) //=> [Ruby Java] 
     fmt.Println(slice) //=> [Ruby Java] //slice2での変更によりsliceの要素も変更されている。
}

スライスのゼロ値

スライスのゼロ値は nil です。
nil スライスは 0 の長さと容量を持っており、何の元となる配列も持っていません。

func main() {
    var slice []int
    fmt.Println(slice, len(slice), cap(slice)) //=> [] 0 0

    if slice == nil {
        fmt.Println("nil!") //=> nil! 
        //sliceの値がnilの場合にnil!を表示する。
    }
}

組み込み関数make()でスライスを作成する

スライスは組み込み関数の make() を利用する事で定義することもできます。

make([]T, len, cap)

上記のmake() 関数の第 1 引数 []T が型、第 2 引数 (len) が 長さ 、第 3 引数 (cap) が 容量 を意味しています。

func main() {
     a := make([]int,5, 5)
     fmt.Println(a) //=> [0 0 0 0 0]
}