どんな記事?
Go言語において、スライスの一部分を切り出してサブスライスを作成する場合、データのコピーを作るのではなく2つの変数がメモリを共有することになるため変更を共有するという記事です。
もしオリジナルとはメモリを共有しない独自のスライスを作成する必要がある場合、make
と組み込み関数のcopy
を使いましょう。
スライスを一部切り出した場合と、copy
関数を使用した場合とでどのような違いがあるか説明していきます。
一部切り出しについて
以下のようにスライスを一部切り出した場合、両者は同じメモリを参照することになります。これはメモリ効率がいいという反面、一方を変えるともう一方にも影響が及ぶことを意味します。
package main
import (
"fmt"
)
func main() {
original := []int{1, 2, 3, 4, 5}
subSlice1 := original[1:3]
subSlice2 := original[2:4]
fmt.Println("Before:", original, subSlice1, subSlice2)
//=> Before: [1 2 3 4 5] [2 3] [3 4]
subSlice1[0] = 20
subSlice2[1] = 40
fmt.Println("After:", original, subSlice1, subSlice2)
//=> After: [1 20 3 40 5] [20 3] [3 40]
}
一部を切り出したサブスライスの変更によって、オリジナルのスライスの値が変更されていることがわかると思います。
また、以下のコードで同一のメモリを参照していることが確認できます。
func main() {
original := []int{1, 2, 3, 4, 5}
subSlice1 := original[1:3]
subSlice2 := original[2:4]
fmt.Printf("original address: %p, subSlice1 address: %p\n", &original[1], &subSlice1[0])
fmt.Printf("original address: %p, subSlice2 address: %p\n", &original[2], &subSlice2[0])
}
copy関数について
copy
関数を使用することで、メモリ領域を共有しない独立したスライスにデータをコピーできます。
func main() {
original := []int{1, 2, 3, 4, 5}
newSlice1 := make([]int, 2)
newSlice2 := make([]int, 2)
copy(newSlice1, original[1:3])
copy(newSlice2, original[2:4])
fmt.Println("Before:", original, newSlice1, newSlice2)
//=> Before: [1 2 3 4 5] [2 3] [3 4]
newSlice1[0] = 20
newSlice2[1] = 40
fmt.Println("After:", original, newSlice1, newSlice2)
//=> After: [1 2 3 4 5] [20 3] [3 40]
}
また、以下のコードで異なるメモリを参照していることが確認できます。
func main() {
original := []int{1, 2, 3, 4, 5}
newSlice1 := make([]int, 2)
newSlice2 := make([]int, 2)
copy(newSlice1, original[1:3])
copy(newSlice2, original[2:4])
fmt.Printf("original address: %p, subSlice1 address: %p\n", &original[1], &newSlice1[0])
fmt.Printf("original address: %p, subSlice2 address: %p\n", &original[2], &newSlice2[0])
}
終わりに
今回はSliceの操作について説明しました。これからもGoについての記事を挙げていこうと思っているので、読んでもらえたら嬉しいです。ここまで読んでいただきありがとうございました。
参考記事