こんにちは。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)について解説しました。
それぞれよく使いますが、慣れるまでは紛らわしいのと、バグが発生しやすい部分でもあると思うので、しっかり違いを理解しながら使い分けるのが大事です。
コメント