Lapis Lazuli

technical blog for web developer

【Go】スライスの仕組みを学ぶ

普段何となく使っていたスライスですが、今回はその仕組みを詳しく勉強してみました。

スライスとは

簡単に言うと、いわゆる可変長配列です。他言語だとListが近いイメージでしょうか。
ここらへんは以前の記事を参照してもらえると幸いです。

スライスの挙動

まず宣言の方法で、

var list []int 

list := make([]int, 10, 10)

というmakeを使う方法があります。
makeは組み込み関数で、type、length、capacityの3つを宣言し作成します。
これだと長さ10、キャパシティ10のスライスが作られていますね。
これが上のほうだと単にスライスを定義しただけなので、どちらも0になっています。

で、ここからが本題なのですが。

このキャパシティを超えるまでappendで増やすと、自動的に2倍のキャパシティを確保して新たにメモリを再割り当てする動作をします。
makeで十分とっている場合は特に問題ないのですが、0て定義するとappendする度に再割り当てが行われます。
これが大きいサイズのスライスでやると実行コストが半端ないので、必ずmakeで宣言するようにしましょう。
確かに長さやキャパシティの宣言が必要なので(キャパシティはオプショナルなので絶対ではないですが・・・)気軽に使ってきた言語(PHPとか)からすると面倒だと思います。
が、やはりGoを使うメリットは、第1にスピードが挙げられると思うので、特に大きいスライスを扱う時はここを意識して書いていきましょう。

makeの挙動

makeで宣言するときに第3引数のキャパシティを省略するとどうなるのでしょうか。
結論からいうと、書き方によって中身が初期化されるかされないかの違いです。

list := make([]int, 5)
// [0 0 0 0 0]
list := make([]int, 0, 5)
// []

という挙動になります。
省略すると長さ=容量になりますので必ず初期化されます。キャパシティを書くと、長さと同一ではないので、仮に0を指定すると空のスライスが出来ます。
どちらがいいのかは一概には言えませんが、自分は省略せずに使うことが多いですね。
やはりセットで宣言しているとわかりやすいので、そうしています。