slice(スライス)
このページでは豊富な例を用いてGolangのsliceの取り扱い方を学ぶことができます。
sliceはGolangの型の1つです。
array(配列)
は固定長(長さを初期化時以降変更できない)ですが、
sliceは可変長の配列の様な特徴(長さを変更できるなど)を持ちます。
しかし、配列とは完全に異なる型であるので取り扱いに注意が必要です。
TL;DR
初期化
var slice1 []int
fmt.Printf("length=%d capacity=%d %v\n", len(slice1), cap(slice1), slice1)
==> length=0 capacity=0 []
slice2 := make([]int, 1)
fmt.Printf("length=%d capacity=%d %v\n", len(slice2), cap(slice2), slice2)
==> length=1 capacity=1 [0]
slice3 := make([]int, 1, 10)
fmt.Printf("length=%d capacity=%d %v\n", len(slice3), cap(slice3), slice3)
==> length=1 capacity=10 [0]
slice4 := []string{"apple", "banana", "orange"}
fmt.Printf("length=%d capacity=%d %v\n", len(slice4), cap(slice4), slice4)
==> length=3 capacity=3 [apple banana orange]
slice5 := []User{{ID: "123", Name: "John", Age: 23}, {"456", "Emma", 20}}
fmt.Printf("length=%d capacity=%d %v\n", len(slice5), cap(slice5), slice5)
==> length=2 capacity=2 [{123 John 23} {456 Emma 20}]
長さ(length)
slice7 := []int{1, 2, 3, 4, 5}
fmt.Println(len(slice7))
==> 5
容量(capacity)
slice8 := make([]int, 5, 7)
fmt.Println(cap(slice8))
==> 7
操作:取得
slice6 := []string{"apple", "banana", "orange"}
fmt.Println(slice6[0])
==> apple
fmt.Println(slice6[2])
==> orange
操作:追加
slice9 := []string{"apple", "banana", "orange"}
// 要素を1つ追加する
appendedSlice9 := append(slice9, "kiwi")
fmt.Println(appendedSlice9)
==> [apple banana orange kiwi]
// 複数の要素を追加する
appendedSlice1 := append(slice9, "peach", "grape")
fmt.Println(appendedSlice1)
==> [apple banana orange peach grape]
// スライスに別のスライスの要素を追加する
anotherSlice := []string{"Go", "Golang"}
appendedSlice2 := append(slice9, anotherSlice...)
fmt.Println(appendedSlice2)
==> [apple banana orange Go Golang]
操作:コピー
slice10 := []int{1, 2, 3}
slice101 := make([]int, 3)
slice102 := make([]int, 5)
copy(slice101, slice10)
copy(slice102, slice10)
fmt.Println(slice101)
==> [1 2 3]
fmt.Println(slice102)
==> [1 2 3 0 0]
操作:切り出し
originalSlice := []string{"apple", "banana", "orange", "grape", "kiwi"}
anotherSlice := originalSlice[2:4]
anotherSlice2 := originalSlice[:3]
fmt.Println(originalSlice)
==> [apple banana orange grape kiwi]
fmt.Println(anotherSlice)
==> [orange grape]
fmt.Println(anotherSlice2)
==> [apple banana orange]
// 配列へのポインタは変更されていないことに注意
originalSlice[2] = "Golang"
fmt.Println(originalSlice)
==> [apple banana Golang grape kiwi]
fmt.Println(anotherSlice)
==> [Golang grape]
fmt.Println(anotherSlice2)
==> [apple banana Golang]
println(&originalSlice[2], &anotherSlice[0], &anotherSlice2[2])
==> 0xc00006e070 0xc00006e070 0xc00006e070
操作:削除
// インデックスがnの要素だけ削除したい
originalSlice1 := []int{1, 2, 3, 4, 5}
n := 2
originalSlice1 = append(originalSlice1[:n], originalSlice1[n+1:]...)
fmt.Println(originalSlice1)
==> [1 2 4 5]
// 元のsliceには副作用を発生させず、特定の要素が削除されたsliceを生成したい場合1
type Person struct {
ID int
Name string
}
originalSlice2 := []Person{{1, "Emma"}, {2, "James"}}
wantedSlice1 := make([]Person, len(originalSlice2))
copy(wantedSlice1, originalSlice2)
for index, person := range wantedSlice1 {
// IDが1のPersonを削除
if person.ID == 1 {
wantedSlice1 = append(wantedSlice1[:index], wantedSlice1[index+1:]...)
}
}
fmt.Println(originalSlice2)
==> [{1 Emma} {2 James}]
fmt.Println(wantedSlice1)
==> [{2 James}]
// 元のsliceには副作用を発生させず、特定の要素が削除されたsliceを生成したい場合2
originalSlice3 := []Person{{1, "Emma"}, {2, "James"}, {3, "David"}}
var wantedSlice2 []Person
for index, person := range originalSlice3 {
// IDが2のPersonを削除
if person.ID == 2 {
wantedSlice2 = append(wantedSlice2, originalSlice3[:index]...)
wantedSlice2 = append(wantedSlice2, originalSlice3[index+1:]...)
}
}
fmt.Println(originalSlice3)
==> [{1 Emma} {2 James} {3 David}]
fmt.Println(wantedSlice2)
==> [{1 Emma} {3 David}]
関連情報:forの使用方法
解説
初期化
sliceの初期化は配列(array)の初期化に似ています。
以下で配列の初期化と比較してみてください。
また、組み込み関数のmake
を使用して初期化することも可能です。
makeを使用する場合、第1引数に型、第2引数に長さ、第3引数に容量を指定します。
波括弧{}
を使用することで初期値を設定することもできます。
// sliceの初期化
var slice1 []int
fmt.Printf("length=%d capacity=%d %v\n", len(slice1), cap(slice1), slice1)
==> length=0 capacity=0 []
// arrayの初期化
var array1 [5]int
fmt.Printf("length=%d capacity=%d %v\n", len(array1), cap(array1), array1)
==> length=5 capacity=5 [0 0 0 0 0]
// 波括弧で初期値を設定
slice2 := []string{"Golang", "Python"}
fmt.Printf("length=%d capacity=%d %v\n", len(slice2), cap(slice2), slice2)
==> length=2 capacity=2 [Golang Python]
// makeを使用(第1引数:型、第2引数:長さ、第3引数:容量)
slice3 := make([]string, 2, 2)
fmt.Printf("length=%d capacity=%d %v\n", len(slice3), cap(slice3), slice3)
==> length=2 capacity=2 [ ]
長さ(length)
組み込み(built-in)関数のlen
を使用して長さを取得することができます。
slice := make([]int, 2, 2)
fmt.Println(len(slice))
==> 2
容量(capacity)
組み込み(built-in)関数のcap
を使用して容量を取得することができます。
slice := make([]int, 2, 3)
fmt.Println(cap(slice))
==> 3
操作:取得
取得したい要素が入っているsliceの後ろに角括弧([])を記述し、その中に取得したい要素のインデックスを指定します。
なお、一番最初の要素のインデックスは0
です。
slice := []string{"Golang", "Python"}
fmt.Println(slice[0])
==> Golang
操作:追加
sliceは組み込み関数のappend
を使用することで新しい要素を追加することができます。
また、appendは可変長引数を持つ関数(variadic function)であり、1回の使用で複数の要素を加えることも可能です。
originalSlice := []string{"original"}
// 要素を1つ追加したい場合
appendedSlice := append(originalSlice, "Gopher")
fmt.Println(originalSlice)
==> [original]
fmt.Println(appendedSlice)
==> [original Gopher]
// 複数の要素を追加したい場合
multipleAppendedSlice := append(originalSlice, "Golang", "Gopher", "GoDoc")
fmt.Println(multipleAppendedSlice)
==> [original Golang Gopher GoDoc]
// 別のスライスの要素を追加したい場合
anotherSlice := []string{"JavaScript", "Java"}
anotherAppendedSlice := append(originalSlice, anotherSlice...)
fmt.Println(anotherAppendedSlice)
==> [original JavaScript Java]
// 元のスライスを変更したい場合
originalSlice = append(originalSlice, "Go")
fmt.Println(originalSlice)
==> [original Go]
操作:コピー
sliceは組み込み関数のcopy
を使用することで他のスライスに値をコピーすることができます。
また、:=
(short variable declarations)を使用してもコピーすることができますが、:=
を使用した場合はコピー元の配列へのポインタもコピーします。
より詳しく理解したい場合は Go Slices: usage and internals を参照してください。
上記の動作を理解した上で適切に使い分けてください。
originalSlice := []int{1, 2, 3, 4}
copiedSlice1 := make([]int, 3, 5)
copiedSlice2 := make([]int, 5)
copy(copiedSlice1, originalSlice)
copy(copiedSlice2 , originalSlice)
fmt.Println(copiedSlice1)
==> [1 2 3]
fmt.Println(copiedSlice2)
==> [1 2 3 4 0]
操作:切り出し
2つのインデックスを:(コロン)で区切ることによりsliceから部分的に要素を切り出したスライスを得ることができます。
インデックスを指定しない場合はコロンの左右で0
と対象のsliceの長さ
がそれぞれ暗黙的に指定されます。
注意するべき点は、切り出したsliceと元のsliceの配列へのポインタが一緒である点です。
副作用を発生させずに処理する必要がある場合はcopy
を使用するなどして対処する必要があります。
originalSlice := []int{0, 1, 2, 3, 4, 5}
anotherSlice1 := originalSlice[2:4]
anotherSlice2 := originalSlice[:3]
fmt.Println(originalSlice)
==> [0 1 2 3 4 5]
fmt.Println(anotherSlice1)
==> [2 3]
fmt.Println(anotherSlice2)
==> [0 1 2]
// インデックスを指定しない場合
fmt.Println(originalSlice[:])
==> [0 1 2 3 4 5]
// 配列へのポインタは変更されていないので、変更したslice以外の2つのsliceの値も変更される
originalSlice[2] = 999
fmt.Println(originalSlice)
==> [0 1 999 3 4 5]
fmt.Println(anotherSlice1)
==> [999 3]
fmt.Println(anotherSlice2)
==> [0 1 999]
println(&originalSlice[2], &anotherSlice1[0], &anotherSlice2[2])
==> 0xc00001a0a0 0xc00001a0a0 0xc00001a0a0
操作:削除
sliceの要素を削除する組み込み関数などは用意されていません。(version 1.15.3 時点)
ここではいくつかの例を挙げるので参考にしてください。
// インデックスが0の要素だけ削除したい
originalSlice1 := []int{1, 2, 3, 4, 5}
originalSlice1 = append(originalSlice1[:0], originalSlice1[0+1:]...)
fmt.Println(originalSlice1)
==> [2 3 4 5]
// 特定の要素が削除された新しいsliceを生成したい場合
type Language struct {
ID int
Name string
}
languageSlice := []Language{{1, "English"}, {2, "Spanish"}, {3, "Chinese"}}
var wantedSlice1 []Language
for index, language := range languageSlice {
// IDが2のLanguageを削除
if language.ID == 2 {
wantedSlice1 = append(wantedSlice1, languageSlice[:index]...)
wantedSlice1 = append(wantedSlice1, languageSlice[index+1:]...)
}
}
fmt.Println(languageSlice)
==> [{1 English} {2 Spanish} {3 Chinese}]
fmt.Println(wantedSlice1)
==> [{1 English} {3 Chinese}]