スライス #
スライスを初期化する #
package main
import (
"fmt"
)
func main() {
// スライス型のデフォルト値は nil
var s0 []int
fmt.Printf("%#v\n", s0) // == []int(nil)
// スライスの初期化
s1 := []int{}
fmt.Printf("%v\n", s1) // == []
// make を使ったスライスの初期化
// make はスライスの初期化関数で make(型, 長さ, 容量) という記法になる
// 長さはそのままスライス長で、容量はあらかじめ確保しておくメモリ量になる
// 容量は省略可能
// 長さや容量があらかじめわかっている場合は指定することでメモリの再割り当てが減り実行効率が良くなる
s2 := make([]int, 2)
s3 := make([]string, 2, 4)
// スライスの要素の初期値はゼロ値と呼ばれるデフォルト値で型によって決まっている
// 数値なら 0 で文字列なら空文字列になる
fmt.Printf("%#v, %#v\n", s2, s3) // == []int{0, 0}, []string{"", ""}
// 長さと容量は len と cap 関数で取得できる
fmt.Printf("s2: len:%d, cap:%d\n", len(s2), cap(s2)) // == s2: len:2, cap:2
fmt.Printf("s3: len:%d, cap:%d\n", len(s3), cap(s3)) // == s3: len:2, cap:4
// 初期化時に値をセットする
s4 := []int{1, 2, 3, 4, 5}
fmt.Printf("%v\n", s4) // == [1 2 3 4 5]
}
スライスの要素の取得と変更と追加をする #
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3, 4, 5}
// 要素を取得する
v := s[0]
fmt.Println(v) // == 1
// 要素を変更する
s[1] = 10
fmt.Printf("%v\n", s) // == [1 10 3 4 5]
// 要素を追加する
s = append(s, 11)
s = append(s, 12)
fmt.Printf("%v\n", s) // == [1 10 3 4 5 11 12]
// 配列の長さを超えるインデックスを指定すると panic になる
s[10] = 4 // => panic: runtime error: index out of range [2] with length 2
}
スライスの要素をループで処理する #
range
を使うとスライスのインデックスと値のペアでループを回せる。
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3, 4, 5}
for i, v := range s {
fmt.Println(i, v)
}
}
スライス同士を結合する #
func append(slice []Type, elems ...Type) []Type
を使いスライス同士を結合する。
elems
は可変長引数であり、これにスライスを渡すために ...
演算子でスライスを展開する。
package main
import (
"fmt"
)
func main() {
s1 := []int{1, 2, 3}
s2 := []int{10, 11, 12}
s3 := append(s1, s2...)
fmt.Printf("%+v", s3) // == [1 2 3 10 11 12]
}
スライスのスライスを定義する #
package main
import (
"fmt"
)
func main() {
s := make([][]int, 2)
for i := 0; i < len(s); i++ {
s[i] = make([]int, 3)
}
fmt.Printf("%#v", s) // == [][]int{[]int{0, 0, 0}, []int{0, 0, 0}}
}
スライスから条件にあった要素を絞り込んで新しいスライスを作る #
package main
import (
"fmt"
)
func main() {
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s2 := []int{}
for _, v := range s1 {
if v%2 == 0 {
s2 = append(s2, v)
}
}
fmt.Printf("%+v", s2) // == [2 4 6 8 10]
}
スライスの一部を別のスライスとして取得する #
スライスの添字を [n:m]
とすると n から m-1 までの部分スライスを返す。
n と m は省略可能で、n を省略すると先頭位置が m を省略すると末尾位置が指定される。
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// s[n:m] でスライス s のインデックス n から m-1 までのスライスを取得する
s1 := s[1:3]
fmt.Println(s1) // == [2 3]
// s[:m] でスライス s のインデックス先頭から m-1 までのスライスを取得する
s2 := s[:2]
fmt.Println(s2) // == [1 2]
// s[n:] でスライス s のインデックス n から末尾までのスライスを取得する
s3 := s[2:]
fmt.Println(s3) // == [3 4 5 6 7 8 9 10]
// 取得したスライスは元のスライスと同じメモリ空間を参照しているので、取得したスライスの値を変更すると元のスライスも変更される
s4 := s[5:]
s4[0] = 20
s4[1] = 21
fmt.Println(s) // == [1 2 3 4 5 20 21 8 9 10]
}
スライスをソートする #
スライスのソートには要素の型によって専用の関数が用意されており、専用の関数がない型は汎用の関数が利用できる。
整数スライスのソートには sort.Ints
を用い、浮動小数点数スライスのソートには sort.Float64s
が用いられる。
汎用ソート関数は sort.Slice
である。
package main
import (
"fmt"
"sort"
)
func main() {
// Ints で整数スライスのソート
s1 := []int{2, 3, 6, 8, 1, 5, 4, 7, 9}
sort.Ints(s1)
fmt.Println(s1) // == [1 2 3 4 5 6 7 8 9]
// Float64s で浮動小数点数スライスのソート
s2 := []float64{3.1, 1.1, 0.01, -5.25}
sort.Float64s(s2)
fmt.Println(s2) // == [-5.25 0.01 1.1 3.1]
// Slice で関数を使って任意のスライスを任意の要素でソート
s3 := []string{"apple", "cat", "orange", "elephant"}
sort.Slice(s3, func(i, j int) bool {
// 文字列長の降順に
return len(s3[i]) > len(s3[j])
})
fmt.Println(s3) // == [elephant orange apple cat]
}
参考ドキュメント: sort
スライスの値をユニークにする #
スライスの値をユニークにしたい場合は、スライスの値をキーとするマップを作成し、マップのキーの一覧から改めてスライスを作る。
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3, 2, 1, 4, 1, 3}
m := map[int]bool{}
for _, v := range s {
m[v] = true
}
u := []int{}
for k, _ := range m {
u = append(u, k)
}
fmt.Println(u) // == [1 2 3 4]
}
スライスの要素を削除する(append を使った場合) #
package main
import (
"fmt"
)
func main() {
// 削除対象のスライス
s := []int{1, 2, 3, 4, 5}
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 3 4 5] len:5, cap:5
// インデックス 2 の要素を削除する
i := 2
// スライスの要素削除用の組み込み関数は無い
// 削除対象の要素の前後の部分スライスを結合して新しいスライスを作ることで、結果的に対象の要素を削除する
s1 := s[:i] // => [1, 2]
s2 := s[i+1:] // => [4, 5]
s = append(s1, s2...)
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 4 5] len:4, cap:5
// 1行で書くと
s = []int{1, 2, 3, 4, 5}
s = append(s[:i], s[i+1:]...)
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 4 5] len:4, cap:5
}
スライスの要素を削除する(copy を使った場合) #
append を使ったスライスの要素の削除はメモリの再割り当てが行われるため実行効率が悪い。 copy を使うことで効率的にスライスの要素の削除ができる。
package main
import (
"fmt"
)
func main() {
// 削除対象のスライス
s := []int{1, 2, 3, 4, 5}
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 3 4 5] len:5, cap:5
// インデックス 2 の要素を削除する
i := 2
// 削除対象以後の部分スライスを取得
s1 := s[i:] // => [3, 4, 5]
// 削除対象より後ろの部分スライスを取得
s2 := s[i+1:] // => [4, 5]
// s1 を s2 で上書きして削除対象を潰す
copy(s1, s2)
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 4 5 5] len:5, cap:5
// 末尾要素が重複しているので切り詰める
s = s[:len(s)-1]
s = []int{1, 2, 3, 4, 5}
// 2行で書くとこう
copy(s[i:], s[i+1:])
s = s[:len(s)-1]
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 4 5] len:4, cap:5
s = []int{1, 2, 3, 4, 5}
// copy の返り値がコピーした要素数なので1行でも書ける
s = s[:i+(copy(s[i:], s[i+1:]))]
fmt.Printf("%v len:%d, cap:%d\n", s, len(s), cap(s)) // == [1 2 4 5] len:4, cap:5
}
スライスからランダムに値を取得する #
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
rand.Seed(time.Now().UnixNano())
fmt.Println(s[rand.Intn(len(s))])
}