言語バージョン 1.15.3

公開日 November 6, 2020, GMT+9

更新日 January 12, 2021, GMT+9

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}]

1次情報

Source file src/runtime/slice.go

Go Slices: usage and internals