slice 에서 length 와 capacity 에 대하여

YoungHyo Choi·2024년 1월 11일
0

100 Go Mistakes

목록 보기
1/1
post-thumbnail

100 Go Mistakes and How to Avoid Them
Not understanding slice length and capacity (#20)
slice 의 length 와 capacity 를 이해하는 것은 고퍼에게 매우 중요한 정보다.

length & capacity

기본 개념

  • length 는 slice 에 있는 원소의 수
  • capacity 는 해당 slice 가 가질 수 있는 사이즈
  • make 함수를 사용해서 생성한다.
// int 를 원소로 가지고, length=3, capactiy=6 인 슬라이스
s := make([]int, 3, 6)
fmt.Println(s) // [0 0 0]
  • length 만큼의 해당 slice type의 default value를 가지는 슬라이스를 생성한다.

slice 의 element 추가

s[1] = 1
fmt.Println(s) // [0 1 0]

s[3] = 3 // panic: runtime error: index out of range [3] with length 3

s = append(s, 3)
fmt.Println(s) // [0 1 0 3]

s = append(s, 4, 5)
fmt.Println(s) // [0, 2, 0, 4, 3, 4, 5]

s = append(s, 4, 5, 6)
fmt.Println(s) // [0 1 0 3 4 5 6]
  • length 로 초기화된 slice 의 경우 해당 length 안에서 value 를 변경 할 수 있다.
  • 하지만 인덱스가 넘어가는 경우, panic 이 발생한다.
  • length 로 선언한 뒤에 원소를 추가하고 싶다면 append 함수를 사용

  • append 를 통해 원소를 추가하다가 capacity 가 넘어가는 경우, go 에서는 기존 capacity 의 2배를 가지는 array 를 새로 생성하고 해당 array 에 append 로 받은 원소를 추가.
    slice 는 새로운 array 를 참조하게 된다.
    (기존 array 는 참조되지 않는 경우, GC 에 의해서 해제된다.)

slice 재할당에서의 주소 관리

s1 := make([]int, 3, 6) // Three-length, six-capacity slice
s2 := s1[1:3] // Slicing from indices 1 to 3

fmt.Println(s1) // [0, 0, 0]
fmt.Println(s2) // [0, 0]
  • slicing 을 통해 새로운 slice 를 할당해주는 경우, 같은 array 를 참조하지만, length 와 capacity 가 달라진다.
s1[1] = 1
fmt.Println(s1) // [0, 1, 0]
fmt.Println(s2) // [1, 0]

s2 = append(s2, 2)
fmt.Println(s1) // [0, 1, 0]
fmt.Println(s2) // [1, 0, 2]
  • 같은 array 를 참조하고 있기 때문에 length 에 포함되는 원소를 변경하는 경우, 같이 변하게 된다.
s2 = append(s2, 3)
s2 = append(s2, 4) // backing array 가 가득차는 경우
s2 = append(s2, 5)

  • append 를 하다가 capacity 가 넘어가게 되는 경우, 위에서 언급했듯이 새로운 array 를 생성하고 slice 에 할당하기 때문에 서로 다른 array 를 참조하게 된다.

정리

  • slice 선언시 capacity 는 잘 선언을 해주자.
  • 개인적으로는 length 도 선언해주는게 좋다고 생각한다.
type MyModel struct {
	field1 string
    field2 int
}

a := []MyModel{}
for _, m := range asdf {
	a = append(a, m)
}

go를 처음 사용할 때 별 생각없이 이런 식으로 많이 사용했었는데 빈 값으로 초기화 됐기 때문에 array capacity doubling 과 메모리 재할당이 계속 일어나는 코드였을 것 같다. (인지하고서는 make를 사용해서 선언해주기는 했지만)
하지만 capacity 를 2배씩 늘리고, 메모리를 재할당하고, slicing을 했을 때 같은 array 를 참조하고 이런 내용은 몰랐다.
기본적인 내용이지만 항상 숙지하고 습득해서 개발하도록 하자

profile
golang과 elasticsearch를 좋아하는 3년차 백엔드 엔지니어입니다.

0개의 댓글