Slice
C++의 Vector, Java의 ArrayList 등과 같이 사용할 수 있는 Go의 Slice.
코딩 테스트든 개발이든 가장 많이 사용하게 될 자료구조가 아닌 듯싶다.
그렇기에 Slice를 잘 사용하기 위해 기본적으로 알아야 하는 내용들의 이해를 돕기 위해 Array와 비교하여 요약하려 한다.
Array와 Slice의 차이
기본적으로 Array는 memory에 동적으로 할당할 수 없다(정적인 자료구조). 생성 시에 크기를 지정해 주어야 하며 그에 맞춰 const 한 메모리를 할당하게 된다.
하지만 Slice는 그렇지 않다. Slice는 동적으로 할당할 수 있으며 크기를 따로 지정해 주지 않아도 된다(해도 됨).
Array와 Slice 생성 방법
Array
몇 가지의 방법을 소개하겠다. 사실 모두 비슷비슷하니 편한 것을 사용하면 되겠다.
fruits := [3]string{}
fruits[0] = "apple"
fruits[1] = "banana"
fruits[2] = "tomato"
\
fruits := [3]string{"apple", "banana", "tomato"}
\
fruits := [...]string{"apple", "banana", "tomato"} // 배열 안의 데이터 수에따라 Array의 크기가 정해진다.
\
//Go에서는 전역으로 변수를 설정할 경우에 shortcut 방식인 ':='기호를 사용할 수 없다.
var fruits [3]string
fruits[0] = "apple"
fruits[1] = "banana"
fruits[2] = "tomato"
Slice
var fruits = make([]string, 3, 4) // make의 매개변수 첫 번째는 Slice의 type을 의미한다. 두 번째는 길이를 의미한다. 세 번째는 크기를 의미한다.
fruits[0] = "apple"
fruits[1] = "banana"
fruits[2] = "tomato"
\
var fruits = make([]string, 3) // Slice의 크기를 의미하는 세 번째 매개변수는 생략 가능하며 이 경우 길이로 설정한 값과 같이 크기가 설정된다.
fruits[0] = "apple"
fruits[1] = "banana"
fruits[2] = "tomato"
\
fruits := []string{"apple", "banana", "tomato"}
\
fruits := []string{}
fruits = append(fruits, "apple")
fruits = append(fruits, "banana")
fruits = append(fruits, "tomato")
\
fruits := []string{}
fruits = append(fruits, "apple", "banana", "tomato")
\
var fruits []string
fruits = append(fruits, "apple")
fruits = append(fruits, "banana")
fruits = append(fruits, "tomato")
방법이 더 있을 수 있지만 이 정도면 충분할 듯..
Array와 Slice의 순회 방법
Array와 Slice 순회 방법은 같다.
index 사용
for i := 0; i < len(fruits); i++ {
fmt.Println(fruits[i])
}
range 사용
for _, food := range fruits {
fmt.Println(food)
}
Slice의 memory 사용 방법
make를 사용하여 Slice를 생성하는 것을 위에서 보였다. memory를 사용하는 것에서 make의 세 번째 매개변수인 Slice의 크기가 중요하게 작용한다.
Slice는 구조체로 이루어져 있다.
type slice structure{
Elem* // 요소가 저장된 시작 메모리
len // slice의 길이
cap // slice의 크기
}
Go에서 Slice에 값을 추가하기 위해선 append 함수를 사용하는데 윗 코드에서 보면 같은 변수에 재 할당해 주는 모습을 볼 수 있다. 이를 보면 append 함수를 적용한 이후의 slice와 기존의 slice는 다른 값을 가진다는 것을 유추할 수 있을 것이다. 그렇다면 어떻게 다른 값을 가질까? 두 가지를 생각해 볼 수 있다.
- cap>len
- cap<=len
아래 코드로 확인해 보자.
var numbers = make([]int, 2, 3)
numbers[0] = 1
numbers[1] = 2
fmt.Printf("numbers의 길이 : %d numbers의 크기 : %d numbers의 주소 : %p\n\n", len(numbers), cap(numbers), numbers)
numbers = append(numbers, 3)
fmt.Printf("numbers의 길이 : %d numbers의 크기 : %d numbers의 주소 : %p\n\n", len(numbers), cap(numbers), numbers)
numbers = append(numbers, 4)
fmt.Printf("numbers의 길이 : %d numbers의 크기 : %d numbers의 주소 : %p\n\n", len(numbers), cap(numbers), numbers)
len이 cap보다 작을 때 append 함수를 적용하면 주소가 그대로 인 것을 확인할 수 있다.
그러나 len이 cap과 같을 때는 전혀 다른 주소를 가리키는 것을 확인할 수 있다.
이렇게 원래 할당받은 메모리의 크기가 가득 차게 된다면 크기가 2배가 되고 새로운 memory를 할당받는 것을 알 수 있다.
'Study > Go' 카테고리의 다른 글
[Go] 고루틴(Goroutine) 알고쓰기(1) - 동시성, 병렬성 프로그래밍 그리고 고루틴 (0) | 2021.09.01 |
---|---|
[Go] Json 활용 방법 (0) | 2021.08.12 |
[Go] 웹 서버(Web Server) 열기 (0) | 2021.07.27 |
[Go] GC(Garbage Collector) (0) | 2021.07.18 |
[Go] C++ 과 Go 포인터의 차이 (0) | 2021.07.17 |