読者です 読者をやめる 読者になる 読者になる

Go言語入門 / golang 入門 スライスとキャパシティについて詳しく解説

f:id:nasust:20161122141513p:plain The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) CC3.0

こんにちはnasustです。

Go言語 / golangのSliceについて詳しく解説します。

Sliceとは

Sliceとは配列の参照と長さを持っています。Sliceは配列と同じ様にアクセスできるのでSliceと意識せずに操作することが出来ます。

しかし配列とまったく同様に扱うとバグの元になりますので注意が必要です。

numbers := []int{ 1 , 2 , 3 , 4 , 5 , 6}

上記の例で、初期かで何が起こるか解説します。

  1. Sliceはキャパシティが6の配列を用意します。
  2. Sliceは配列の参照を持っています。
  3. Sliceの長さは6になります。
  4. Sliceは初期値をセットします。

次にappend関数で要素を追加した時の振る舞いを解説します。

キャパシティを超える様に追加

numbersA := []int{ 1 , 2 , 3 , 4 , 5 , 6} //キャパシティ 6
numbersB := append( numbersA , []int{ 7 , 8} ... ) //キャパシティ超える
cap(numbersA)//キャパシティ 6 lenは6
cap(numbersB)//キャパシティ 12 lenは8

numbersAにappend関数でキャパシティを超える様に追加すると、numbersAの2倍の配列のキャパシティを持ったnumbersBを返します。

何故、numbersBはnumbersAの2倍の配列のキャパシティを用意するのかは要素の追加時に効率を良くする為です。

例えばnumbersB Sliceに一個の要素を追加した場合、キャパシティの範囲内ですので、新たに配列を拡張せずに追加することができます。

もしキャパシティが無い場合は毎回メモリのアロケーションする為に負荷が掛かります。

キャパシティを超え無い様に追加した時は配列の参照は元のまま

numbersA := []int{ 1 , 2 , 3 , 4 , 5 , 6} //キャパシティ 6
numbersB := append( numbersA , []int{ 7 , 8} ) //キャパシティ超える
cap(numbersA)//キャパシティ 6 lenは6
cap(numbersB)//キャパシティ 12 lenは8
numbersC := append( numbersB , []int{ 9 , 10 } ... ) //キャパシティ超えない。numbersBと同じ配列を参照している。lenは10
numbersC[0] = 99

fmt.Println("A:" , numbersA ) //A:  [1 2 3 4 5 6]
fmt.Println("B:" , numbersB ) //B:  [99 2 3 4 5 6 7 8]
fmt.Println("C:" , numbersC ) //C:  [99 2 3 4 5 6 7 8 9 10]

上記の例では、append関数でnumbersBにキャパシティを超えない様に要素を追加した新しいnumbersCが出来ました。そしてnumbersC[0] = 99しています。この時、numbersBとnumbersCはキャパシティを超えていないので、同じ配列を参照しています。ですのでnumbersB[0]も99になります。

まとめ

Sliceを扱う場合は、キャパシティを意識してSliceがどの配列を参照しているか注意しましょう。
Sliceを関数に値渡して操作した場合、キャパシティの問題で把握できなくなる可能性があります。 その時はポインタで渡して解決することが出来ますが、ややこしいので関数内では操作しないか、または配列をコピーしたSliceを使用するようにしましょう。


Go言語 / golang 入門 目次に戻る

広告を非表示にする