Search

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

요약
Go Collections
썸네일
https://noticon-static.tammolo.com/dgggcrkxq/image/upload/v1566913552/noticon/xjarxsfmmcouhih40val.png
작성일자
2024/04/30 01:21
수정일자
2024/04/30 08:23
스택
Go
카테고리
기초
태그
array
slice
map

컬렉션 (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
복사
강의 목록

참조