개발자의 오르막

Golang 2차원 배열 문제를 통한 array, slice 의 개념정리 본문

GoLang

Golang 2차원 배열 문제를 통한 array, slice 의 개념정리

계단 2023. 5. 6. 10:56

 

 

이번 문제의 키워드는 Array, Slice, Make 이다. 해당 문제에서 하나의 배열을 2차원으로 만드는 방법으로는 슬라이스의 특징인 배열의 부분을 추출하는 기능을 활용하는 문제였습니다.

 

정답을 반환하는 answer 2차원 배열을 만들고, param으로 주어지는 num_list 1차원 배열을 특정 길이만큼 잘라서 할당하는 풀이입니다.

 

그러면 위의 세 키워드 Array, Slice, Make 에 대해 개념을 정리해보겠습니다.

 

 

Array


  • Golang 에서 배열은 동일한 데이터 타입의 고정 크기 요소들의 순서화된 집합이다.
  • 배열은 대괄호 [] 안에 크기를 지정하여 선언된다.
    • var myArray [5] int
    • myArray := [5]int{1,2,3,4,5}
    • myArray := […]int{6,7,8,9,10}
      • 배열의 요소 개수 만큼 크기가 지정된다.
  • 배열에서 값을 할당할 때 index 와 value 값을 대입하는 방법으로 할당한다.
    • myArray[0] = 1
  • 배열의 크기는 선언 시에 결정되며, 변경할 수 없다. 즉, 배열의 크기가 고정되어 있으므로 요소를 추가하거나 제거할 수 없다.
  • 따라서 배열은 선언하자마자 모든 인덱스는 초기값을 가지게 된다.
    • var myArray [5]int → [0 0 0 0 0 ]

 

 

Slice


  • Golang 에서의 슬라이스는 배열을 기반으로 한 동적 크기의 배열이다.
  • 슬라이스는 대괄호안에 인덱스가 없는 형태로 선언된다.
    • var mySlice []int
  • 슬라이스는 가변크기의 배열이기 때문에 최초 선언 시 빈 값을 가지게 된다.
    • var mySlice []int → []
    • 따라서, 슬라이스의 초기값을 지정하기 않고 선언만 하면 Nil slice 가 된다.
  • 슬라이스는 값을 대입할 때 요소에 값을 할당하는 방법과 append 함수를 사용하여 새로운 요소를 추가하는 방법이 있다.
    • mySlice[0] = 5
    • mySlice = append(mySlice, 5)
  • 슬라이스는 배열의 일부분을 슬라이스로 생성할 수 있다.
myArray := [5]int{1,2,3,4,5}
mySlice := myArray[1:4]

 

 

Slice 추가, 병합, 복사

 


append (추가, 병합)

  • append() 는 Golang 의 내장된 함수 (builtin.go) 이다.
  • 슬라이스는 append() 함수를 이용해서 데이터를 추가할 수 있다.
  • 용량 내에서는 길이를 변경하여 데이터를 추가하며, 용량이 초과하는 경우에는 설정한 용량만큼 새로운 배열을 생성하고, 기존 배열 값들을 모두 새 배열에 복제한 후 다시 슬라이스를 할당하는 방식이다.

 

# 슬라이스에 요소를 추가하는 방법

a := []int{1,2,3}
a = append(a, 4)

fmt.Println(a) -> [1 2 3 4]

 

# 두 슬라이스를 병합하는 방법

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

fmt.Println(a) -> [1 2 3 4 5 6]

 

 

copy (복사)

  • copy() 는 Golang 의 내장된 함수 (builtin.go) 이다.
  • 슬라이스는 copy() 함수를 이용해서 데이터를 복사할 수 있다.
  • copy(붙여넣을 슬라이스, 복사할 슬라이스)
a := []int{1,2,3}
b := make([]int, len(a), cap(a)*2)
copy(a, b)

fmt.Println(a) -> [1 2 3 4 5 6]
  • b의 용량이 a보다 큼으로, copy 를 할 때 a의 요소를 모두 가지게된다.

 

a := []int{1,2,3}
b := []int{4,5}
copy(b, a)

fmt.Println(a) -> [1 2]
  • b의 용량이 a보다 작거나 같을 때, b는 a의 요소들을 덮어씌우게 된다.

 

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

fmt.Println(b) -> [2]

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

fmt.Println(b) -> [1,2]
  • 슬라이스 := 복사할 슬라이스(복사할 첫 인덱스 : 복사할 마지막 인덱스+1)
  • 앞의 첫 인덱스가 없을 경우 0부터 복사한다.

 

 

Make


  • Make 함수는 Golang 의 내장된 함수(builtin.go)로, slice, map, channel 등의 빈(empty) 데이터 구조를 생성하고 초기화할 때 사용됩니다.
  • make(T, args)
    • T는 Make 함수로 생성하고자 하는 데이터 타입을 나타냅니다.
    • args 는 데이터 구조를 초기화하기 위한 인자(argument)를 나타냅니다.
  • s := make([]int, 5, 10)
    • 위의 코드는 []int 타입의 길이가 5이며, 용량이 10인 슬라이스를 선언한 것을 의미합니다.
    • 길이가 5로 정해져있으므로, 초기 선언된 슬라이스는 [0 0 0 0 0] 값을 지니고 있습니다.
  • s := make(map[string]int, 10)
    • 위의 코드는 map[string]int 타입의 용량이 10인 슬라이스를 선언한 것을 의미합니다.

 

 

 

 

 

 

Array vs Slice


먼저 Array 와 Slice 에 대해 아래 표와 같이 차이점이 정리되어 있습니다.

 

 

타입


  • 위의 표에 보면 배열의 타입은 값(value) 이며, 슬라이스는 참조(reference) 로 되어있다.
  • 배열은 하나의 값으로 불면의 성격을 가지며, 상수라고도 생각될 수 있다.
a := [3]int{1,2,3}
b := a
b[0] = 4
fmt.Println(a) -> [1 2 3]
fmt.Println(b) -> [4 2 3]

 

  • 반면에, 슬라이스는 배열의 일부분으로서, 참조하기 때문에 포인터를 사용한다.
  • 슬라이스는 다른 변수에 할당하면 원래의 배열을 참조하게 된다.
a := []int{1,2,3}
b := a
b[0] = 4
fmt.Println(a) -> [4 2 3]
fmt.Println(b) -> [4 2 3]

 

 

용량과 길이


  • 용량(capacity) 은 데이터 구조가 저장할 수 있는 최대 수를 나타내며, 길이(lencgh) 는 데이터 구조에 저장된 요소의 수를 의미한다.
  • 따라서 배열의 경우 선언 시 요소의 크기가 정해지고, 값이 불변하기 때문에 용량과 길이가 정해진다.
  • 그러나 슬라이스의 경우 요소의 수가 동적으로 크기가 조정가능한 배열이기 때문에 용량과 길이가 변할 수 있다.

 

 

비교연산


  • 배열의 타입은 값이며, 슬라이스의 타입은 참조입니다.
  • 따라서, 동일한 두 배열을 비교할 때는 값 끼리의 비교이므로 true 가 될 수 있습니다.
a := [3]int{1,2,3}
b := [3]int{1,2,3}

fmt.Println(a == b) -> true
  • 그러나 슬라이스는 배열의 참조이기 때문에, 두 슬라이스를 비교하는 것은 불가능하며, 두 슬라이스의 요소들의 일치여부는 비교할 수 있습니다.

 

 

메모리할당


  • 배열은 선언 시부터 배열의 고정된 크기가 정해지기 때문에 컴파일 시점에서 메모리가 할당됩니다.
  • 슬라이스는 동적으로 크기가 결정되기 때문에 메모리 공간이 달라질 수 있어 런타임 시점에서 메모리가 할당됩니다. 원래 슬라이스의 용량보다 많은 요소가 할당될 경우에는 더 큰 공간을 할당하고 기존 슬라이스의 요소를 복사하는 작업이 진행됩니다.

 

 

 

 

 

Reference


Comments