以前レビューしていたときに、capを指定した方が良い理由を説明したら分かりやすいと言っていただいたので、嬉しくなってQiitaにも書きます。
以下のように、全てのUserのうち特定のUserだけを取り出したい場合を想定します。
type User struct {
ID int64
Name string
Status int8 // 0 -> InActive, 1 -> Activeとする
}
func main() {
users := []*User{
&User{
ID: 0,
Name: "user0",
Status: 0,
},
&User{
ID: 1,
Name: "user1",
Status: 1,
},
&User{
ID: 2,
Name: "user2",
Status: 0,
},
...
}
// 1. activeUsers := []*User{}
// 2. activeUsers := make([]*User, 0, len(users))
// 1と2はどっちが良いか?
for _, u := range users {
// activeなユーザーだけ取り出したい
if u.Status == 1 {
activeUsers = append(activeUsers, u)
}
}
}
activeUsersのの大きさはusersのlenよりも小さくなる可能性が高い(上記の例の場合は間違いなく小さい)ので、capを指定したらその分無駄な容量を確保するのではないか?という疑問を持っていたようです。
- 1(capを指定しない)の場合
package main
import (
"fmt"
)
func main() {
l := []int{}
for i := 0; i < 17; i++ {
l = append(l, i)
fmt.Println("len: ", len(l), ", cap: ", cap(l))
}
}
-------------- result --------------
len: 1 , cap: 2
len: 2 , cap: 2
len: 3 , cap: 4
len: 4 , cap: 4
len: 5 , cap: 8
len: 6 , cap: 8
len: 7 , cap: 8
len: 8 , cap: 8
len: 9 , cap: 16
len: 10 , cap: 16
len: 11 , cap: 16
len: 12 , cap: 16
len: 13 , cap: 16
len: 14 , cap: 16
len: 15 , cap: 16
len: 16 , cap: 16
len: 17 , cap: 32
https://play.golang.org/p/-lxFdnmrz0z
capが足りなくなった場合に2倍のcapを確保するので、大きくなればなるほど無駄にメモリ確保する可能性が高い
- 2(capを指定する)の場合
package main
import (
"fmt"
)
func main() {
l := make([]int, 0, 20)
for i := 0; i < 17; i++ {
l = append(l, i)
fmt.Println("len: ", len(l), ", cap: ", cap(l))
}
}
-------------- result --------------
len: 1 , cap: 20
len: 2 , cap: 20
len: 3 , cap: 20
len: 4 , cap: 20
len: 5 , cap: 20
len: 6 , cap: 20
len: 7 , cap: 20
len: 8 , cap: 20
len: 9 , cap: 20
len: 10 , cap: 20
len: 11 , cap: 20
len: 12 , cap: 20
len: 13 , cap: 20
len: 14 , cap: 20
len: 15 , cap: 20
len: 16 , cap: 20
len: 17 , cap: 20
https://play.golang.org/p/7Q1TW_CF2-A
実際のlenがcapに満たない場合でも、倍確保されるよりはメモリ使わないで済むし、確保するための処理が入らないのでより良い
ということで、こういう場合はcapを指定しましょう!