配列
固定長のデータ構造です。
// 配列の宣言
var numbers [5]int // ゼロ値で初期化
fmt.Println(numbers) // [0 0 0 0 0]
// 初期値付き
primes := [5]int{2, 3, 5, 7, 11}
fmt.Println(primes) // [2 3 5 7 11]
// 要素数を自動推論
vowels := [...]string{"a", "e", "i", "o", "u"}
fmt.Println(len(vowels)) // 5
// 要素へのアクセス
fmt.Println(primes[0]) // 2
primes[0] = 1
配列の走査
numbers := [5]int{1, 2, 3, 4, 5}
// インデックスと値
for i, v := range numbers {
fmt.Printf("index: %d, value: %d\n", i, v)
}
// 値のみ
for _, v := range numbers {
fmt.Println(v)
}
配列は値型
original := [3]int{1, 2, 3}
copy := original // コピーが作成される
copy[0] = 100
fmt.Println(original) // [1 2 3](変更されない)
fmt.Println(copy) // [100 2 3]
スライス
可変長のデータ構造です。配列の参照として動作します。
// スライスの作成
var slice []int // nil スライス
fmt.Println(slice == nil) // true
// リテラルで作成
numbers := []int{1, 2, 3, 4, 5}
// makeで作成
slice2 := make([]int, 5) // 長さ5、容量5
slice3 := make([]int, 3, 10) // 長さ3、容量10
スライス操作
numbers := []int{1, 2, 3, 4, 5}
// 要素へのアクセス
fmt.Println(numbers[0]) // 1
numbers[0] = 10
// 長さと容量
fmt.Println(len(numbers)) // 5
fmt.Println(cap(numbers)) // 5
// 追加
numbers = append(numbers, 6)
numbers = append(numbers, 7, 8, 9)
// スライスを追加
more := []int{10, 11, 12}
numbers = append(numbers, more...)
スライス式
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
// 部分スライス
fmt.Println(numbers[2:5]) // [2 3 4]
fmt.Println(numbers[:3]) // [0 1 2]
fmt.Println(numbers[7:]) // [7 8 9]
fmt.Println(numbers[:]) // [0 1 2 3 4 5 6 7 8 9]
// 3インデックス式(容量を制限)
limited := numbers[2:5:7] // [2 3 4]、容量5
スライスのコピー
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
dst[0] = 100
fmt.Println(src) // [1 2 3](変更されない)
fmt.Println(dst) // [100 2 3]
スライスの削除
numbers := []int{0, 1, 2, 3, 4, 5}
// インデックス2の要素を削除
i := 2
numbers = append(numbers[:i], numbers[i+1:]...)
fmt.Println(numbers) // [0 1 3 4 5]
マップ
キーと値のペアを格納するデータ構造です。
// マップの作成
var m map[string]int // nil マップ
// リテラルで作成
scores := map[string]int{
"太郎": 85,
"花子": 92,
}
// makeで作成
m = make(map[string]int)
// 要素の追加・更新
scores["次郎"] = 78
// 要素の取得
score := scores["太郎"]
fmt.Println(score) // 85
// 存在確認
score, ok := scores["三郎"]
if ok {
fmt.Println(score)
} else {
fmt.Println("存在しません")
}
// 削除
delete(scores, "次郎")
// 長さ
fmt.Println(len(scores))
マップの走査
scores := map[string]int{
"太郎": 85,
"花子": 92,
"次郎": 78,
}
for key, value := range scores {
fmt.Printf("%s: %d\n", key, value)
}
// キーのみ
for key := range scores {
fmt.Println(key)
}
マップとスライスの組み合わせ
// スライスを値に持つマップ
groups := map[string][]string{
"fruits": {"apple", "banana", "orange"},
"vegetables": {"carrot", "potato"},
}
groups["fruits"] = append(groups["fruits"], "grape")
// マップのスライス
users := []map[string]string{
{"name": "太郎", "email": "taro@example.com"},
{"name": "花子", "email": "hanako@example.com"},
}
実践例:単語カウント
package main
import (
"fmt"
"strings"
)
func wordCount(text string) map[string]int {
counts := make(map[string]int)
words := strings.Fields(strings.ToLower(text))
for _, word := range words {
counts[word]++
}
return counts
}
func main() {
text := "Go is great Go is simple Go is fast"
counts := wordCount(text)
for word, count := range counts {
fmt.Printf("%s: %d\n", word, count)
}
}
実践例:スタックとキュー
package main
import "fmt"
// スタック(LIFO)
type Stack []int
func (s *Stack) Push(v int) {
*s = append(*s, v)
}
func (s *Stack) Pop() (int, bool) {
if len(*s) == 0 {
return 0, false
}
index := len(*s) - 1
value := (*s)[index]
*s = (*s)[:index]
return value, true
}
// キュー(FIFO)
type Queue []int
func (q *Queue) Enqueue(v int) {
*q = append(*q, v)
}
func (q *Queue) Dequeue() (int, bool) {
if len(*q) == 0 {
return 0, false
}
value := (*q)[0]
*q = (*q)[1:]
return value, true
}
func main() {
// スタック
var stack Stack
stack.Push(1)
stack.Push(2)
stack.Push(3)
v, _ := stack.Pop()
fmt.Println(v) // 3
// キュー
var queue Queue
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
v, _ = queue.Dequeue()
fmt.Println(v) // 1
}
まとめ
- 配列は固定長、型に長さが含まれる
- スライスは可変長、配列の参照
makeでスライス・マップを初期化appendでスライスに要素追加copyでスライスをコピー- マップは
map[KeyType]ValueType - 存在確認は
value, ok := m[key] rangeでイテレート
次回は構造体について学びます。