Go는 배열과 슬라이스 2개의 자료형을 제공한다.
슬라이스
배열은 한 번 초기화된 길이를 수정할 수 없다.
슬라이스는 {포인터, len, cap} 3가지 값을 가진다.
- 포인터: 실제 배열이 저장되어 있는 주소 값을 가리키는 포인터
- len: (length) 실제 데이터가 저장된 길이
- cap: (capacity) 데이터를 저장할 수 있는 최대 길이
만약 cap이 5이고 len이 3인 슬라이스가 있다면 아래와 같은 형태이다.
x | x | x |
슬라이스에 내용을 추가할 때, cap에 빈자리가 있으면 빈자리에 내용을 채우고, 그렇지 않으면 새로운 배열을 생성한 후 가리킨다. 자세한 내용은 아래에 기록하였다.
초기화
// 배열
arr := [...]int{1, 2, 3}
arr := [3]int{1, 2, 3}
// 슬라이스
slice := []int{1, 2, 3} // len: 3, cap: 3
slice := make([]int, 3) // len: 3, cap: 3
slice := make([]int, 3, 5) // len: 3, cap: 5
배열은 [길이]를 명시해주거나 [...]을 이용해 자동으로 지정해 준다. 반면 슬라이스는 []과 같이 작성해 생성한다. 이때 cap을 지정하지 않으면 len과 동일하게 초기화된다.
slice := make([]int, 3)
fmt.Println(len(slice), cap(slice)) // 3 3
len()함수와 cap() 함수를 통해 확인할 수 있다.
인덱싱 및 슬라이싱
arr := [...]int{1, 2, 3}
arr[1] // 2
arr[:1] // {1, 2}
arr[:] // {1, 2, 3}
배열과 슬라이스 모두 [ ]을 이용해 요소를 가져올 수 있다.
배열 & 슬라이스 관계
func main() {
// 슬라이스는 포인터를 전달
array := [2]int{0, 1}
slice := []int{0, 1}
ModifyArr(array)
ModifySlice(slice)
fmt.Println(array) // [0 1]
fmt.Println(slice) // [999 1]
}
func ModifyArr(arr [2]int) {
arr[0] = 999
}
func ModifySlice(slice []int) {
slice[0] = 999
}
파라미터로 전달할 때, 배열은 복사하고 슬라이스는 참조한다. 앞에서 봤듯 슬라이스는 배열의 포인터 객체이다. 따라서 arr은 main과 ModifyArr에서 다른 배열을 사용하고 있고, slice는 main과 ModifySlice에서 같은 슬라이스를 사용하고 있다.
array := [...]int{0, 3, 5, 5, 1}
slice := array1[:3]
array1[0] = 1000
fmt.Println(array1) // [1000 3 5 5 1]
fmt.Println(slice1) // [1000 3 5]
fmt.Println(len(slice1), cap(slice1)) // 3 5
배열을 수정하였는데 슬라이스까지 변경된 것을 볼 수 있다. 슬라이스를 배열로부터 가져올 경우, 복사가 아닌 참조를 수행한다.
append
append(추가할 배열, 추가할 값...)
append는 슬라이스에 값을 추가한다.
slice := []int{1, 2, 3}
slice = append(slice, 99, 77)
fmt.Println(slice) // [1 2 3 99 77]
복사
앞에서 봤듯 [:]의 형태로 슬라이싱 한다고 실제 복사가 이루어지지는 않는다.
// 1
newSlice := append([]int{}, arr...)
// 2
newSlice := make([]int{})
copy(newSlice, arr)
따라서 새로운 슬라이스를 생성하고 값을 모두 수정하는 형식으로 복사해야 한다.
삭제
func main() {
slice := []int{1, 2, 3, 4, 5}
slice = delete(slice, 3)
fmt.Println(slice) // [1 2 3 5]
}
func delete(slice []int, index int) []int {
slice = append(slice[:index], slice[index+1:]...)
return slice
}
삭제할 값을 제외한 두 슬라이스를 append하는 형식으로 처리할 수 있다.
삽입
func main() {
slice := []int{1, 2, 3, 4, 5}
slice = insert(slice, 3, 99)
fmt.Println(slice) // [1 2 3 99 4 5]
}
func insert(slice []int, index int, value int) []int {
slice = append(slice, 0)
copy(slice[index+1:], slice[index:])
slice[index] = value
return slice
}
중간에 내용을 삽입하기 위해 슬라이스에 공간을 생성한 후 해당 인덱스의 내용을 수정하는 방식으로 구현하였다.