PR

Go言語(Golang)の配列(array)・スライス(slice)・マップ(map)の使い方や違いについて

基礎

こんにちは。Tomoyuki(@tomoyuki65)です。

Go言語(Golang)で複数のデータを扱う際は配列(array)、スライス(slice)、マップ(map)を利用しますが、使い方や違いについて迷うことがあると思います。

この記事では、そんなGo言語の配列(array)、スライス(slice)、マップ(map)について解説します。

 

Go言語(Golang)の配列(array)・スライス(slice)・マップ(map)の使い方や違いについて

配列(array)

まず配列(array)は、同じ型の固定長の要素を持つ集まりです。例としては以下のようになります。

package main

import (
  "fmt"
)

func main() {
  // 変数宣言と代入
  var arr1 [3]int
  arr1[1] = 2
  fmt.Println("arr1:", arr1)

  // 変数宣言と初期化と代入
  var arr2 [5]int = [5]int{2, 1, 4, 0, 2}
  arr2[0] = 9
  fmt.Println("arr2:", arr2)

  // 初期化と代入
  arr3 := [4]int{5, 6, 1, 2}
  arr3[2] = 7
  fmt.Println("arr3:", arr3)

  // 空の配列による初期化と代入
  arr4 := [2]int{}
  arr4[1] = 5
  fmt.Println("arr4:", arr4)

  // 長さはコンパイラが推論
  arr5 := [...]int{1, 2, 3, 4, 5}
  arr5[3] = 1
  fmt.Println("arr5:", arr5)
}

 

実行結果

arr1: [0 2 0]
arr2: [9 1 4 0 2]
arr3: [5 6 7 2]
arr4: [0 5]
arr5: [1 2 3 1 5]

 

スライス(slice)

次にスライス(slice)は、同じ型の可変長の要素を持つ集まりです。例としては以下のようになります。

package main

import (
  "fmt"
)

func main() {
  // 変数宣言と要素の追加
  var slice1 []int
  slice1 = append(slice1, 6)
  fmt.Println("slice1:", slice1)

  // 変数宣言と初期化と要素の追加と代入
  var slice2 []int = []int{2, 5, 9}
  slice2 = append(slice2, 0)
  slice2[0] = 1
  fmt.Println("slice2:", slice2)

  // 初期化と要素の追加と代入
  slice3 := []int{5, 6, 1, 2}
  slice3 = append(slice3, 2)
  slice3[3] = 7
  fmt.Println("slice3:", slice3)
  
  // 空の配列による初期化と要素の追加と代入
  slice4 := []int{}
  slice4 = append(slice4, 9)
  slice4[0] = 1
  fmt.Println("slice4:", slice4)

  // 配列をスライスに変換
  arr := [...]int{1, 2, 3, 4, 5}
  slice5 := arr[:]
  slice5 = append(slice5, 1)
  slice5[0] = 7
  fmt.Println("slice5:", slice5)
}

※append関数は新しいスライスを返す

 

実行結果

slice1: [6]
slice2: [1 5 9 0]
slice3: [5 6 1 7 2]
slice4: [1]
slice5: [7 2 3 4 5 1]

 

尚、slice型は参照渡しなので、関数のパラメータに渡した後などに要素を更新すると、元の値も変更されるので注意(バグになりやすい)しましょう。

package main

import (
  "fmt"
)

// sliceの要素を更新する関数
func modifySlice(s []int) {
  s[1] = 0
}

func main() {
  // 初期化と要素の追加と代入
  originalSlice := []int{1, 2, 3, 4, 5}
  fmt.Println("originalSlice:", originalSlice)

  // 関数でsliceの要素を更新
  modifySlice(originalSlice)

  // 関数実行後のoriginalSlice
  fmt.Println("originalSlice:", originalSlice)
}

 

実行結果

originalSlice: [1 2 3 4 5]
originalSlice: [1 0 3 4 5]

 

元の値を変更させないようにしたい場合は、コピーしてからパラメータに渡すようにします。

package main

import (
  "fmt"
)

// sliceの要素を更新する関数
func modifySlice(s []int) {
  s[1] = 0
}

func main() {
  // 初期化と要素の追加と代入
  originalSlice := []int{1, 2, 3, 4, 5}
  fmt.Println("originalSlice:", originalSlice)

  // sliceをコピー
  copySlice := []int{}
  copySlice = append(copySlice, originalSlice...)

  // 関数でsliceの要素を更新
  modifySlice(copySlice)

  // 関数実行後のoriginalSlice
  fmt.Println("originalSlice:", originalSlice)
}

 

実行結果

originalSlice: [1 2 3 4 5]
originalSlice: [1 2 3 4 5]

 

スポンサーリンク

マップ(map)

次にマップ(map)は、キーと値のペアを格納する連想配列(ハッシュテーブル)です。

package main

import (
  "fmt"
)

func main() {
  // 変数宣言とmake関数による初期化と要素の追加
  var map1 map[string]string
  map1 = make(map[string]string)
  map1["key1"] = "value1"
  fmt.Println("map1:", map1)

  // 変数宣言と値ありの初期化と要素の追加
  var map2 map[string]string = map[string]string{
    "key2": "value2",
    "key3": "value3",
  }
  map2["key4"] = "value4"
  fmt.Println("map2:", map2)

  // make関数による初期化と要素の追加
  map3 := make(map[string]string)
  map3["key5"] = "value5"
  fmt.Println("map3:", map3)

  // 初期化と要素の追加
  map4 := map[string]string{}
  map4["key6"] = "value6"
  fmt.Println("map4:", map4)

  // 値ありの初期化と要素の追加
  map5 := map[string]string{
    "key7": "value7",
  }
  map5["key8"] = "value8"
  fmt.Println("map5:", map5)
}

 

実行結果

map1: map[key1:value1]
map2: map[key2:value2 key3:value3 key4:value4]
map3: map[key5:value5]
map4: map[key6:value6]
map5: map[key7:value7 key8:value8]

 

尚、map型は参照渡しなので、関数のパラメータに渡した後などに要素を更新すると、元の値も変更されるので注意(バグになりやすい)しましょう。

package main

import (
  "fmt"
)

// mapの要素を更新する関数
func modifyMap(m map[string]string) {
  m["key2"] = "change"
}

func main() {
  // map型の値を初期化
  originalMap := map[string]string{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3",
  }
  fmt.Println("originalMap:", originalMap)

  // 関数でmapの要素を更新
  modifyMap(originalMap)

  // 関数実行後のoriginalMap
  fmt.Println("originalMap:", originalMap)
}

 

実行結果

originalMap: map[key1:value1 key2:value2 key3:value3]
originalMap: map[key1:value1 key2:change key3:value3]

 

元の値を変更させないようにしたい場合は、コピーしてからパラメータに渡すようにします。

package main

import (
  "fmt"
)

// mapの要素を更新する関数
func modifyMap(m map[string]string) {
  m["key2"] = "change"
}

func main() {
  // map型の値を初期化
  originalMap := map[string]string{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3",
  }
  fmt.Println("originalMap:", originalMap)

  // mapをコピー
  copyMap := make(map[string]string)
  for key, value := range originalMap {
    copyMap[key] = value
  }

  // 関数でmapの要素を更新
  modifyMap(copyMap)

  // 関数実行後のoriginalMap
  fmt.Println("originalMap:", originalMap)
}

 

実行結果

originalMap: map[key1:value1 key2:value2 key3:value3]
originalMap: map[key1:value1 key2:value2 key3:value3]

 

ちなみにGoのバージョン1.21以上からはmapsパッケージが追加され、これを使ってコピーすることが可能になっています。

package main

import (
  "fmt"
)

// mapの要素を更新する関数
func modifyMap(m map[string]string) {
  m["key2"] = "change"
}

func main() {
  // map型の値を初期化
  originalMap := map[string]string{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3",
  }
  fmt.Println("originalMap:", originalMap)

  // mapsパッケージによりコピー
  copyMap := make(map[string]string)
  maps.Copy(copyMap, originalMap)

  // 関数でmapの要素を更新
  modifyMap(copyMap)

  // 関数実行後のoriginalMap
  fmt.Println("originalMap:", originalMap)
}

 

実行結果

originalMap: map[key1:value1 key2:value2 key3:value3]
originalMap: map[key1:value1 key2:value2 key3:value3]

 

スポンサーリンク

最後に

今回はGo言語の配列(array)、スライス(slice)、マップ(map)について解説しました。

それぞれよく使いますが、慣れるまでは紛らわしいのと、バグが発生しやすい部分でもあると思うので、しっかり違いを理解しながら使い分けるのが大事です。

 

この記事を書いた人
Tomoyuki

SE→ブロガーを経て、現在はWeb系エンジニアをしています!

Tomoyukiをフォローする
基礎
スポンサーリンク
Tomoyukiをフォローする

コメント

タイトルとURLをコピーしました