Search

[Go 기초] [#8] 컬렉션 (배열, 슬라이스, 맵)

작성일시
2024/04/30 01:21
수정일시
2024/06/01 03:04
스택
Go
카테고리
Tutorial
태그
array
slice
map
1 more property

컬렉션 (Collection)

컬렉션이란, 여러 값을 하나의 변수 아래에서 관리할 수 있는 데이터 구조를 일컫습니다.
Go의 주요 컬렉션 타입에는 배열, 슬라이스, 맵, 채널이 있습니다. 이번 강의에서는 채널을 제외한 배열, 슬라이스, 맵에 대해서 알아보도록 하겠습니다.
💡
채널에 대한 내용은 추후에 있을 고루틴 강의에서 함께 다루겠습니다.

배열 (Array)

Go 언어에서 배열은 같은 타입의 요소들을 순서대로 저장하는 데 사용됩니다.
배열은 고정된 크기를 가지며, 배열의 크기는 타입의 일부로 간주됩니다. 따라서 Go 언어에서 배열로 선언된 변수는 길이를 변경할 수 없습니다.
package main import "fmt" func main() { var arr [3]int // 정수형 배열을 선언하는 방법입니다. // 여기서 배열의 길이는 3입니다. arr[0] = 1 // 배열의 첫 번째 요소(인덱스 0)에 1을 할당합니다. arr[1] = 2 // 두 번째 요소(인덱스 1)에 2를 할당합니다. arr[2] = 3 // 세 번째 요소(인덱스 2)에 3을 할당합니다. fmt.Println("Array:", arr) // 배열의 내용을 출력합니다. } // 실행 결과 : // Array: [1 2 3]
Go
복사

슬라이스 (Slice)

슬라이스는 Go 언어에서 가장 많이 사용되는 형태이며 배열보다 더 유용합니다.
슬라이스는 배열과 유사하지만, 그 크기가 동적이기 때문에 크기에 대한 제약사항을 신경쓰지 않아도 됩니다.
package main import "fmt" func main() { slice := make([]int, 0) // 정수형 슬라이스를 생성합니다. // 초기 길이는 0입니다. slice = append(slice, 1) // 슬라이스에 요소를 추가합니다. 여기서는 1을 추가합니다. slice = append(slice, 2) // 슬라이스에 2를 추가합니다. slice = append(slice, 3) // 슬라이스에 3을 추가합니다. slice[2] = 9 // 슬라이스의 참조 방법은 배열과 같습니다. fmt.Println("Slice:", slice) // 슬라이스의 내용을 출력합니다. } // 실행 결과 : // Slice: [1 2 9]
Go
복사

용량 (Capacity) / 길이 (Length)

슬라이스를 사용할 때는 용량길이를 구분해야 합니다.
용량은 슬라이스가 저장할 수 있는 요소의 최대 개수를 의미하며, 길이는 실제로 슬라이스에 저장된 요소의 개수를 의미합니다.
package main import "fmt" func main() { // 길이가 3, 용량이 5인 슬라이스 생성 slice := make([]int, 3, 5) fmt.Printf("초기 슬라이스: 길이=%d 용량=%d %v\n", len(slice), cap(slice), slice) // 슬라이스에 요소 추가 slice = append(slice, 4) fmt.Printf("1개 요소 추가 후: 길이=%d 용량=%d %v\n", len(slice), cap(slice), slice) // 슬라이스에 더 많은 요소 추가 slice = append(slice, 5, 6, 7) fmt.Printf("3개 요소 추가 후: 길이=%d 용량=%d %v\n", len(slice), cap(slice), slice) } // 실행 결과 : // 초기 슬라이스: 길이=3 용량=5 [0 0 0] // 1개 요소 추가 후: 길이=4 용량=5 [0 0 0 4] // 3개 요소 추가 후: 길이=7 용량=10 [0 0 0 4 5 6 7]
Go
복사
예제 코드를 통해 알 수 있듯이 길이는 슬라이스에 저장된 요소의 수를 나타내고, 용량은 슬라이스가 수용할 수 있는 최대 요소의 수를 나타냅니다.
슬라이스의 용량이 초과된다고 판단되면 런타임은 슬라이스의 용량을 자동으로 조정하여 더 많은 요소를 저장할 수 있도록 늘려주는 것을 확인할 수 있습니다.

모든 요소 순회

슬라이스를 사용하다보면 슬라이스의 모든 요소를 순회하여야 하는 경우가 많습니다.
이 경우 for range 키워드를 이용하면 매우 편하게 모든 요소를 순회할 수 있습니다.
package main import ( "fmt" ) func main() { slice := []int{10, 11, 12, 13, 14} // for-range 문 안에서 슬라이스 변수를 참조할 경우 // 첫번째 값은 요소의 인덱스 리터럴 값, 두번째 값은 요소의 값을 반환하며 // 슬라이스 전체 요소를 순회합니다. for index, value := range slice { fmt.Println("index : ", index) fmt.Println("value : ", slice[index], value) fmt.Println() } } // 실행 결과 : // index : 0 // value : 10 10 // // index : 1 // value : 11 11 // // index : 2 // value : 12 12 // // index : 3 // value : 13 13 // // index : 4 // value : 14 14
Go
복사

맵 (Map)

맵은 key-value 쌍을 저장하는 데 사용되는 자료구조입니다.
맵은 해쉬 테이블에 기반한 키 값을 통해 보관된 데이터를 찾는 자료구조이기 때문에 속도가 매우 빠릅니다. (통상적으로 Go 언어의 built-in map 컬렉션의 성능은 O(1+n/k) 로 알려져 있습니다.)
맵은 임의의 타입에 대한 key 와 value 를 가질 수 있으며 key 의 경우 비교 가능한 형태라면 어떤 데이터 타입이든 가능합니다.
Go 언어에서 맵은 map 키워드를 사용하여 선언합니다.
package main import "fmt" func main() { m := make(map[string]int) // 문자열을 키로, 정수를 값으로 가지는 맵을 생성합니다. m["one"] = 1 // 맵에 "one"이라는 키와 1이라는 값을 추가합니다. m["two"] = 2 // 맵에 "two"라는 키와 2라는 값을 추가합니다. fmt.Println("Map:", m) // 맵의 내용을 출력합니다. } // 실행 결과 : // Map: map[one:1 two:2]
Go
복사

nil Map

var 키워드를 이용하여 변수를 선언할 때, 타입이 레퍼런스 타입에 속할 경우 Go 언어에서는 그 변수가 담길 공간에 대한 할당을 해주지 않습니다.
따라서 var 키워드로 map 을 선언할 경우, 이를 nil map 이라고 부르며 해당 map 변수는 변수의 주소 공간을 가르킬 뿐 기본적으로는 아무것도 없는 것과 같기 때문에 변수를 사용할 경우 오류가 발생합니다.
package main import ( "fmt" ) func main() { // 문자열을 키로, 정수를 값으로 가지는 맵을 생성합니다. // 이 경우 실제 map 변수에 대한 공간을 할당하지 않기 때문에 // nil map 입니다. var m map[string]int // 맵에 "one"이라는 키와 1이라는 값을 추가합니다. // nil map 참조은 비어 있는 변수에 대한 참조와 같기 때문에 // 프로그램이 실행되면 해당 라인에서 즉시 런타임 패닉이 발생합니다. m["one"] = 1 fmt.Println("Map:", m) } // 실행 결과 : // panic: assignment to entry in nil map
Go
복사

Key 검사 방법

map을 사용할 때, 맵에 존재하지 않는 key 를 사용할 경우 Go 언어는 기본값을 반환합니다.
이 경우 프로그램이 의도한 동작과는 다른 동작으로 행동할 수 있기 때문에 Key 에 대한 Value 가 존재하는지 확인하는 방법을 알면 이러한 경우를 방지할 수 있습니다.
package main import "fmt" func main() { m := make(map[string]int) m["one"] = 1 m["two"] = 2 // 맵에 "three"라는 키를 가지는 요소가 있는지 확인합니다. // 존재할 경우엔 exists 값이 true 이며 아닐 경우엔 false 로 반환됩니다. // 존재할 경우엔 value 값이 맵에 저장된 값이 되며, 아닐 경우엔 기본 값이 반환됩니다. value, exists := m["three"] if !exists { fmt.Println("three is not exists!") // "three"라는 키를 가지는 요소가 없기 때문에 이 문장을 출력하고 프로그램을 종료합니다. return } fmt.Println("Three : ", value) } // 실행 결과 : // three is not exists!
Go
복사

모든 요소 순회

맵도 모든 요소를 순회하고 싶을때 for range 키워드를 사용할 수 있습니다.
💡
Go 언어에서 map은 unordered map 이기 때문에 값이 저장된 순서와 순회하는 순서가 서로 같다고 보장할 수 없습니다.
package main import "fmt" func main() { m := make(map[string]int) m["one"] = 1 m["two"] = 2 m["three"] = 3 // for-range 문 안에서 맵 변수를 참조할 경우 // 첫번째 값은 key, 두번째 값은 그 key와 쌍을 이룬 value 를 반환하며 맵 전체를 순회합니다. for key, value := range m { fmt.Println("key : ", key) fmt.Println("value : ", m[key], value) fmt.Println() } } // 실행 결과 : // key : one // value : 1 1 // // key : two // value : 2 2 // // key : three // value : 3 3
Go
복사
강의 목록

참조