Goのsliceからデータを生成するイメージがつかなかった話
はじめに
Go言語を学び始めると、スライス(slice)に関する理解でつまずくことがあります。特にmake関数やcap関数の役割が分かりづらかったり、どういう使い方をするのかイメージがつきにくいと思います。そこで本記事では、Goのスライスを深堀りし、makeとcapの役割や使用シーンを具体例とともに解説します。
そもそもsliceってなに?
スライス(slice)は、Go言語の配列を柔軟に扱うために設計されたデータ型です。スライスは以下の3つの要素から構成されます。
- データへの参照: スライスは内部的に配列を参照します。データそのものを持つのではなく、データへのポインタを保持しています。
- 長さ(Length): スライスが現在保持している要素数。len()関数で取得可能です。
- 容量(Capacity): スライスが内部で保持する配列の最大サイズ。cap()関数で取得可能です。
以下のコードで、実際にスライス作ってみます。
make([]T, len, cap)
スライスといえばmake関数、くらいの覚え方でもいいくらいよく使われます。
- T: スライスの要素の型(例:int、stringなど)
- len: スライスの初期長さ
- cap: スライスの容量(省略可能。省略するとlenと同じ値になる)
次のコードは、長さ3、容量5、int型のスライスを生成します。
n := make([]int, 3, 5)
fmt.Printf("len=%d cap=%d value=%v\n", len(n), cap(n), n)
出力結果は以下
len=3 cap=5 value=[0 0 0]
- 長さが3のスライスnが生成され、初期値として0が設定されています。
- 容量が5であるため、スライスにはあと2個の要素を追加できます。
cap関数の役割
cap関数は、スライスの容量を取得します。スライスは容量を超えると内部で新しい配列を割り当てて要素をコピーするため、容量を意識しておくと効率的にメモリを管理できます。
n := make([]int, 3, 5)
fmt.Println(cap(n)) // 出力: 5
スライスの生成方法まとめ
- 配列から生成
array := [5]int{1, 2, 3, 4, 5}
s := array[1:4] // 配列の部分を参照
fmt.Println(s) // 出力: [2 3 4]
- make関数で生成
s := make([]int, 3, 5)
fmt.Println(s) // 出力: [0 0 0]
- リテラルで生成
s := []int{1, 2, 3}
fmt.Println(s) // 出力: [1 2 3]
スライスの動的な成長
スライスに要素を追加するにはappendを使用します。容量を超えた場合、新しい配列が内部的に生成され、既存のデータがコピーされます。
s := make([]int, 3, 5) // 長さ3、容量5のスライス
s = append(s, 10, 20) // 容量内なので新しい配列は生成されない
fmt.Println(s) // 出力: [0 0 0 10 20]
容量を超える場合:
s = append(s, 30, 40) // 容量を超えたため新しい配列が生成される
fmt.Println(s) // 出力: [0 0 0 10 20 30 40]
スライス ≠ 「切り取る」がややこしい
スライスは、元の配列やスライスを部分的に参照できる機能があるため、「切り取る」というイメージが名前に反映されています。しかし、make関数を使用すれば新しいスライスも生成できるため、「切り取る」だけではない汎用的なデータ構造として理解する必要があります。
スライスと配列の違い
特徴 | 配列 | スライス |
---|---|---|
長さ | 固定 | 可変 |
型情報 | 長さも型に含まれる | 長さは型に含まれない |
参照型か値型 | 値型(完全コピー) | 参照型 |
初期化方法 | リテラルや[...]T{} | リテラル、make関数 |
動的拡張 | 不可能 | 可能 |
実用例:効率的なデータ処理
データの初期化と追加
以下、データを動的に収集する例です。
func collectData() []int {
data := make([]int, 0, 10) // 初期容量を10に設定
for i := 0; i < 15; i++ {
data = append(data, i)
}
return data
}
出力結果
data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
collectData関数内で、スライスを初期化しつつ、生成してます。関数内なので、ショート形式で書いてます。その後の、for文でスライスに要素を追加してます。
まとめ
Go言語におけるスライスは、柔軟で効率的なデータ操作を実現するための強力なデータ型です。makeはスライスを生成するために使われ、capは容量を確認して効率的なメモリ管理を助けます。これらを正しく理解することで、Goプログラムの性能を最大限に引き出すことができます。
スライスの理解はGo言語の第一歩。ぜひ実際にコードを書いて感覚を掴んでみてください〜
Discussion