컬렉션

최승훈·2020년 8월 8일
0

Golang기초

목록 보기
2/7
post-thumbnail

컬렉션이란?

Golang에서의 컬렉션이란, 두 개 이상의 변수를 모아놓은 것을 뜻함.
다수의 데이터를 저장하고 처리하는 경우에 유용하게 쓰임.
컬렉션의 종류
1. 배열(Array)
2. 슬라이스(Slice)
3. 맵(Map)

1. 배열(Array)

Golang에서의 배열은 정적(고정된 배열 크기)인 특징을 가짐.
배열의 크기를 동적으로 증가시키거나 부분을 발췌하는 등의 기능은 가지고 있지 않음.
왜냐? 고정되어있으니까.

배열의 선언 방법

var 배열이름 [배열크기]자료형

배열의 크기를 데이터타입 앞에 쓰는것은 C언어와 자바같은 다른 언들과의 차이점.
Golang에서의 배열 크기는 자료형을 구성하는 하나의 요소밖에 되지 않음.

[3]int != [5]int

위의 [3]int 와 [5]int는 string과 float처럼 아에 타입 자체가 다른 것.

배열의 사용

package main

import "fmt"

func main() {
    // 길이가 5인 배열 선언
	var arr1 [5]int
    // 0인덱스 값 출력 int형 초깃값은 0이므로 0이 출력됨.
	fmt.Println(arr1[0])

    // 배열 초기화
	arr1 = [5]int{1,2,3,4,5}
    // 배열 전체와 인덱스에 저장된 값들 출력.
  	fmt.Println(arr1, arr1[0], arr1[1])

    // := 를 이용한 배열 선언
	arr2 := [4]int{4,5,6,7}
  	// 인덱스를 이용한 값 초기화
	arr2[0] = 32
	fmt.Println(arr2)

    // [...]을 이용한 배열 크기 자동 설정.
	var arr3 = [...]int{9,8,7,6}
	fmt.Println(arr3, len(arr3))
}

len()함수는 배열의 길이를 구할때 쓰는 Golang의 내장함수.
[...]을 사용하면 배열의 크기를 자동으로 설정
[...]으로 선언했다고해서 이후에 배열의 크기를 바꿀 수 있는 것은 아님.

다차원 배열

	var multiArray [2][3][4]int // 다차원배열 선언(3차원)
	multiArray[1][1][1] = 30 // 인덱스를 이용한 값 초기화

	var a = [3][5]int{
		{1,2,3,4,5},
		{1,2,3,4,5},
		{1,2,3,4,5},
	}
	
	fmt.Println(a[1][2]) //2행 3열의 값 출력

2. 슬라이스(Slice)

배열은 고정된 크기 안에 동일한 데이터를 연속으로 저장해 배열의 크기를 필요데 따라 동적으로 증가또는 부분배열을 발췌하는 등의 기능은 가지고 있지 않음.

슬라이스는 배열과 다르게 고정된 크기를 미리 지정하지 않고, 필요에 따라 크기를 동적으로 변경할 수 있으며, 부분발췌가 가능한 특징을 가짐.

슬라이스의 초기화

슬라이스는 여태껏까지의 자료형과 내부적인 구조적 특징이 다르기 때문에 선언 및 초기화를 할 때 주의를 해야함.

기존의 변수선언
var num int => 한개의 int형 변수가 들어갈 메모리를 만들었다. 라는 의미
Golang에서는 아무런 값을 초기화 하지 않고 선언만 해도 정수,실수는 0, 문자형은 ""이 자동 할당됨.

배열도 마찬가지로 크기를 지정하고 선언하기 때문에 명시한 개수만큼 메모리를 만들어둠.
var arr2 [3]int => 3개의 int타입의 변수가 들어갈 메모리를 만들고, 초기화를 하지 않았기 때문에 자동으로 3개의 메모리엔 0이 할당.
이상태에서 len(arr2)를 했을때 초기화를 하지 않았지만 길이가 3이라는 것을 알 수있는겄은 위와 같은 이유에서 추측이 가능함.

슬라이스에서는 var a []int와 같은 형식으로 선언한다면 배열의 일부분을 가리키는 포인터를 생성함. 초기화를 하지 않았기때문에 슬라이스의 정보만 있는 배열만 생성되고, 실질적인 값들을 담는 메모리는 생성되지 않음.

슬라이스는 크기를 미리 지정하지 않기때문에 컴퓨터가 어디서부터 어디까지 0이나 ""으로 채워야 할지 알 수 없는 상태.
슬라이스의 초기 값을 지정하지 않고 선언만 한다면 Nil Slice(메모리상 크기도 용량도 없는 상태)가 됨.
메모리에 할당받은 영역이 없기때문에 a[0] = 1과 같은 값을 초기화하는것은 불가능함.

하지만, 슬라이스는 아무런 값도 초기화하지 않아도 배열의 위치를 가리키는 포인터(ptr)과
배열의 길이인(len), 전체크기인(cap)의 값을 메모리에 가지고 있음.

때문에 슬라이스를 var a []int와 같이 선언을 할때는 var a []int = []int{1,2,3,4}와 같이 선언과 동시에 초기화 할때만 사용함.
슬라이스를 선언함과 동시에 1,2,3,4를 위한 메모리를 만든다는 뜻.
a[1] = 18과 같이 메모리에 저장돼있는 값을 바꿀 수 있고, 슬라이스의 길이와 용량을 확인하는 함수를 사용 할 수 있음.

슬라이스의 복사

배열은 다른 배열의 값을 대입하면 값 자체가 대입됨.
슬라이스는 참조 타입이기 때문에 슬라이스를 복사해온다는 것은 같은 주소를 참조한다 라는 것과 같은 의미.
따라서 슬라이스는 데이터의 복사 없이 데이터를 사용 할 수 있다는 장점이 있음.

package main

import "fmt"

func main() {

	// 슬라이스 변수 선언
	var a []int
	// 슬라이스 값 설정
	a = []int{1,2,3,4,5}

	// index로 값을 바꿀 수 있음.
	a[1] = 18

	// a의2~5번째 값을 복사해서 l에 대입
	l := a[2:5]

	fmt.Println(l)

	// a[2]를 10 으로 변경.
	a[2] = 10
	fmt.Println(a)
	// l은 슬라이스 a 의 주소를 참조하고있기 때문에 값이 변경됨.
	fmt.Println(l)

	var b []int

	if b == nil {
		fmt.Println("cap", cap(b), "length", len(b), "Nil Slice")
	}
}

make()함수를 이용한 슬라이스 선언

Golang의 내장함수인 make()함수를 이용한 슬라이스의 선언 방법.
슬라이스를 생성함과 동시에 슬라이스의 길이(len), 용량(cap)를 지정하면서 생성이 가능.

용법

make(슬라이스타입, 슬라이스 길이, 슬라이스의 용량)

슬라이스의 용량(Cap)은 생략가능.

길이(len)

초기화된 슬라이스의 요소 개수, 예를들어 슬라이스에 5개의 값이 초기화된다면 길이는5가 됨.
값을 추가하거나 삭제하면 그만큼 길이가 바뀜
len(슬라이스이름)으로 확인가능.

용량(cap)

슬라이스는 길이가 동적으로 늘어나기때문에 길이와 용량을 구분함.
슬라이스가 미리 확보하고있는 메모리의 길이라고 생각하면 편함.

package main

import "fmt"

func main() {

	s := make([]int, 0, 3) // len = 0 , cap = 3 인 슬라이스를 make함수로 선

	for i := 1; i <= 100; i++ {
		s = append(s, i)
        // 길이가 용량과 같아지면 용량이 자동으로 2배씩 늘어나느것을 확인 할 수 있음.(미리 공간확보를 위해.)
		fmt.Println(len(s), cap(s)) // 길이와 용량 확인.
	}

	fmt.Println(s)
}

append() 함수를 이용한 슬라이스 추가 및 슬라이스 병합.

append()함수를 이용해서 슬라이스에 데이터를 추가할 수 있음.
슬라이스에 슬라이스를 추가해서 붙일 수 있음.
슬라이스에 슬라이스를 추가할때는 추가하는 슬라이스 뒤에...을 입력해야 함.

package main

import "fmt"

func main() {

	sl1 := []string{"aaa"}

	// append 함수를 활용한 슬라이스에 데이터 추가.
	sl1 = append(sl1,"bbb")

	
	fmt.Println(sl1)

	a := []int{1, 2, 3}
	b := []int{4, 5, 6}

	// 슬라이스에 슬라이스를 이어붙이기
	// b... 은 => {4,5,6}으로 치환되는 것이기 때문에
	// 사실상 슬라이스에 슬라이스를 추가하는 것이 아닌, a슬라이스에 {4,5,6}이라는 요소들이 추가되는 원리.
	a = append(a , b...)
	fmt.Println(a)
}

copy() 함수를 이용한 슬라이스 복사.

copy(붙여넣을 슬라이스, 복사할 슬라이스)

package main

import "fmt"

func main() {
	a := []int{1,2,3}
	b := make([]int, len(a), cap(a) * 2) // a슬라이스의 2배 용량인 슬라이스 선언.

	copy(b, a) // a를 b에 붙여넣기.
	fmt.Println(b)
	println(len(b), cap(b))
}

slice자르기

슬라이스의 부분만 잘라서 복사하는 방법.

붙여넣을 슬라이스 := 복사할슬라이스[복사할 첫 인덱스:복사할 마지막 인덱스 +1]

package main

import "fmt"

func main() {
	a := make([]int, 0, 3)
	a = append(a, 1,2,3,4,5,6,7)
	fmt.Println(len(a) , cap(a))

	// a 슬라이스의 인덱스1에서 2까지 복사
	b := a[1:3]
	fmt.Println(b)

	// a 슬라이스 인덱스 2부터 끝까지 복사
	c := a[2:]
	fmt.Println(c)

	// a슬라이스의 인덱스 4까지 복사
	d := a[:5]
	fmt.Println(d)

	d[2] = 5

	// 슬라이스 d의 값을 바꿧는데도 a에서 복사한 슬라이스의 해당 인덱스의 값들이 모두 변경됨.
	// 값을 복사해온것이 아닌, 기존 슬라이스 a의 주솟값을 참조하고있음.
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
}

3. 맵(Map)

사전 처럼 "단어:뜻"의 형태를 띄는 컬렉션.
"key:value" 와같은 형태.
슬라이스와 맵의 공통점 => 참조타입(Reference type)

var 맵이름 map[key]value자료형

의 형태로 선언할 수 있음.
var a map[int]string
위와같이 선언만 하고 값을 초기화 하지 않으면 Nil map이 됨.

package main

import "fmt"

func main() {
	var a map[int]string

	if a == nil {
		fmt.Println("Nil map")
	}

	var m = map[string]string{
		"apple" : "red",
		"grape" : "purple",
		"banana" : "yellow",
	}

	fmt.Println(m, len(m))

}

Map의 추가, 갱신, 삭제

맵 컬렉션에 메모리가 할당되면(make()또는{}를 사용한 초기화가 됐으면) 값을 추가, 갱신, 삭제 할 수 있음.

슬라이스는 append()함수를 이용해 값을 추가했지만
맵에서는 "맵이름[key] = value"의 형식으로 값을 추가.
이미 있는 key값에 다른 value값을 저장한다면, 기존의 value는 사라지고 저장한 value값으로 갱신됨.

delete()함수를 이용해 값을 삭제함.
delete(맵이름, 키)
키 값에 해당되는 value가 삭제됨.

package main

import "fmt"

func main() {

	var m = make(map[string]string)
	m["02"] = "서울특별시"
	m["031"] = "경기도"
	m["032"] = "충청남도"
	m["053"] = "대구광역시"

	fmt.Println(m)

	// 동일한 key값으로 value값을 저장하면 갱신.
	m["032"] = "인천"

	fmt.Println(m)

	// m맵의  "031"키 삭제
	delete(m, "031")
	fmt.Println(m)

}

Map의 key 체크와 value 읽기

key가 사용되고있는지? 사용되고있다면 어떤 value값이 저장돼어 있는지 확인하고싶을때..

어떻게 확인?

맵이름[key] 

key에 저장된 value를 반환하고, 해당 키의 값이 존재하는지 않나는지도 확인, 즉 사용하고있는 key값인지 아닌지를 판별해주는 bool값도 반환.

package main

import "fmt"

func main() {

	var m = make(map[string]string)
	m["02"] = "서울특별시"
	m["031"] = "경기도"
	m["032"] = "충청남도"
	m["053"] = "대구광역시"

    // 콘솔출력 함수에 "맵이름[key]를 바로 입력할때는 해당되는 value값만 출력됨.
	fmt.Println(m["032"])
    // /string형태로 존재하지 않는 key값은 ""가 출력.
	fmt.Println(m["02222"], "빈칸요~")
	// value값과 해당 키의 존재여부를 받으려면 변수 두개를 각각 선언해서 받아야함.
	val , exist := m["02"]
	fmt.Println(val, exist)
	// 존재하지 않는 키의 값.
	val ,exist = m["2222"]
	fmt.Println(val, exist)

    // value값만 받고싶을때는 변수 하나만 선언.
	val = m["032"]
	fmt.Println(val)
	// 존재여부만 확인하고싶다면 _, bool 의 형식으로 선언
  	// value값을 받는 인자를 _용법을 써서 생략이 가능함.
	_, exist = m["053"]
	fmt.Println(exist)

	fmt.Println(len(m))


}

출처: https://edu.goorm.io/learn/lecture/2010/%ED%95%9C-%EB%88%88%EC%97%90-%EB%81%9D%EB%82%B4%EB%8A%94-%EA%B3%A0%EB%9E%AD-%EA%B8%B0%EC%B4%88/lesson/81410/%EB%A7%B5-map

profile
안녕하세요

0개의 댓글