Array and Slice

Go를 공부하는데 ArraySlice의 사용법이 거의 같은데 무슨 차이가 있는지에 대해 정리한 바이다.


Array

정해진 크기가 있는, 데이터를 순서대로 저장하는 자료구조이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
arr1 := [4]int{1, 2, 3}
fmt.Println(arr1) // [1, 2, 3, 0]

// array 크기 자동 지정
arr2 := [...]int{1, 2, 3, 4, 5}
fmt.Println(arr2) // [1, 2, 3, 4, 5]

// slicing
arr3 := arr2[1:3]
fmt.Println(arr3) // [2, 3]

// iterating
for i, num := range arr3 {
fmt.Println(i, num)
}

Slicing을 할 때 파이썬과의 차이는 음수는 사용할 수 없다. (index는 음수가 될 수 없다고 compile error가 뜬다)
마지막 2개를 가져올 때는 아래와 같이 사용한다.

1
2
3
arr := [...]int{1, 2, 3, 4, 5}
lastTwo := arr[len(arr) - 2:len(arr)]
fmt.Println(lastTwo) // [4, 5]

Slice

Array와 사용 방법은 같지만 크기를 동적으로 변경하는 등 유용한 기능들을 제공한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// array에 기초하지만 고정된 크기를 미리 지정하지 않을 수 있고 동적으로 변경 가능하다
slice1 := []int{1, 2, 3} // literal assigning
fmt.Println(slice1) // [1, 2, 3]

// making slice
slice2 := make([]int, 5, 10) // length 5, capacity 10
fmt.Println(slice2) // [0, 0, 0, 0, 0]
fmt.Println(len(slice2), cap(slice2)) // 5, 10
//fmt.Println(slice2[7]) // panic: out of range

// appending
slice3 := append([]int{1}, 2, 3, 4)
fmt.Println(slice3) // [1, 2, 3, 4]
fmt.Println(len(slice3), cap(slice3)) // 4, 4

// copy
slice4 := make([]int, len(slice3), cap(slice3)*2)
copy(slice4, slice3) // copy slice3 data into slice4
fmt.Println(slice4) // [1, 2, 3, 4]

Slice 내부구조

내부 배열을 가리키는 배열 포인터와, length, capacity를 갖는다.

make([]byte, 5)는 다음과 같은 내부구조를 갖는다.

List와 같이 capacity에 길이에 상관없이 자료형이 필요하다면 slice를 계속 append를 해서 사용하면 된다.
capacity는 내부적으로 관리된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// https://github.com/golang/go/blob/87e48c5afdcf5e01bb2b7f51b7643e8901f4b7f9/src/runtime/slice.go#L100-L112
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}

참고:

Share