スライス

スライス #

スライスを初期化する #

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]
}
play_circleRun open_in_newRun In The Playground

スライスの要素の取得と変更と追加をする #

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
}
play_circleRun open_in_newRun In The Playground

スライスの要素をループで処理する #

range を使うとスライスのインデックスと値のペアでループを回せる。

package main

import (
	"fmt"
)

func main() {
	s := []int{1, 2, 3, 4, 5}

	for i, v := range s {
		fmt.Println(i, v)
	}
}
play_circleRun open_in_newRun In The Playground

スライス同士を結合する #

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]
}
play_circleRun open_in_newRun In The Playground

スライスのスライスを定義する #

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}}
}
play_circleRun open_in_newRun In The Playground

スライスから条件にあった要素を絞り込んで新しいスライスを作る #

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]
}
play_circleRun open_in_newRun In The Playground

スライスの一部を別のスライスとして取得する #

スライスの添字を [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]
}
play_circleRun open_in_newRun In The Playground

スライスをソートする #

スライスのソートには要素の型によって専用の関数が用意されており、専用の関数がない型は汎用の関数が利用できる。 整数スライスのソートには 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]
}

play_circleRun open_in_newRun In The Playground

参考ドキュメント: 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]
}
play_circleRun open_in_newRun In The Playground

スライスの要素を削除する(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
}
play_circleRun open_in_newRun In The Playground

スライスの要素を削除する(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
}
play_circleRun open_in_newRun In The Playground

スライスからランダムに値を取得する #

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

play_circleRun open_in_newRun In The Playground

参考ドキュメント: math/rand , time