Help us understand the problem. What is going on with this article?

Goのsliceでcapを指定した方が良いケース

以前レビューしていたときに、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を指定しましょう!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away