Go/Golang - Slice
Slice 기본
Go 에서 제공하는 동적배열
1
2
3
4
| var slice1 = []int{1,2,3} // {} 를 통한 초기화
var slice2 = make([]int, 3) // make() 를 통한 초기화
// make(배열을 가르키는 포인터, 요소 개수, 실제 배열의 길이)
var slice2 = make([]int, 3, 5) // make() 를 통한 초기화2
|
Slice 순회
1
2
3
4
5
| // range 순회
for i, v := range slice1 {
slice1[i] = v*2
fmt.Println(slice1[i])
}
|
Slice 추가
1
2
3
4
5
6
7
8
| // append로 추가
// append()함수가 호출되면 슬라이스에 값을 추가할 수 있는 빈 공간이 있는지 확인한다. (cap - len)
// (중요!) 빈공간이 없다면 새로운 큰 배열(일반적으로 2배 크기)을 마련한다. -> 이후 기존 배열 요소를 새로운 배열에 복사한다. -> 새로운 배열 맨 뒤에 새 값을 추가한다.
// 예를 들어, slice1 과 slice2 가 같은 배열주소를 가르키고 있는 상황에서, slice1,2의 len,cap 상황에 따라 append를 적용했을 떄 다른 나머지 slice의 결과에도 영향이 갈 수 있다.
// (ex Slice1은 빈공간이 없지만 Slice2는 빈공간이 있는데, Slice2에 append했더니 빈공간이 없는 Slice1의 값도 바뀜)
slice3 := append(slice2, 4)
slice3 := append(slice2, 3,4,5,6,7)
slice_origin = append(slice_origin, 14, 13, 14)
|
Slice 동작 원리
1
2
3
4
5
6
7
8
| // SliceHeader의 모습
type SliceHeader struct {
Data uintptr // 배열 포인터
Len int // 요소 개수
Cap int // 배열 길이
}
// 슬라이스 변수 대입 시 배열에 비해 사용되는 메모리나 속도에 이점이 있다.
// 아래 동작 차이 참조
|
Slice 와 Array 동작차이
Slice
와 Array
는 동작 방식에 차이가 있다. Go 언어에서 모든 값에 대한 대입은 복사로 이루어진다. Slice
는 포인터 필드를 비롯한 SliceHeader가 복사되고 Array
는 값의 복사가 이루어 진다. 따라서 아래와 같이 array
[1, 2, 3, 4, 5] / slice
[1, 2, 200, 4, 5] 처럼 값이 다르게 나타나게 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // Array <-> Slice 차이
func changeArray(array [5]int) {
array[2] = 200
}
func changeSlice(slice []int){
slice[2] = 200
}
func main(){
array1 := [5]int{1,2,3,4,5}
slice1 := []int{1,2,3,4,5}
changeArray(array1)
changeSlice(slice1)
fmt.Println("array: ", array1)
fmt.Println("slice: ", slice1)
// array: [1 2 3 4 5]
// slice: [1 2 200 4 5]
}
|
Slicing > 배열의 일부를 집어내는 기능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Slice 기능을 사용하면 배열은 그대로 len 5로 존재하고, SliceHeader의 값만 바뀐다.
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:2]
// 예를 들면 SliceHeader -> uintptr: array주소, Len: 1, Cap: 4 이다.
arrayToSlice := array[:] // 전체 슬라이스. 배열 전체를 슬라이스로 만들고 싶을 때 주로 사용한다.
// cap 사이즈까지 바꾸고 싶은 경우에는 인덱스 3개로 Slicing한다.
slice1 := []int{1, 2, 3, 4, 5}
slice2 := slice1[1:3:4]
fmt.Println(slice2) // [2 3]
fmt.Println(len(slice2)) // 2
fmt.Println(cap(slice2)) // 3
|
Slice 기능 활용
슬라이스 복제
두 Slice가 같은 배열을 가리키면 여러가지 문제가 발생한다. 따라서 제일 좋은 방법은 Slice를 복제하는 방법이다.
1
2
3
4
5
6
7
8
9
10
11
| // copy sol 1.
for i, v := range slice1 {
slice2[i] = v
}
// copy sol 2.
slice2 = append([]int{}, slice1...)
// copy sol 3.
result := copy(slice2, slice1)
append() 함수 개선하기
|
슬라이스 요소 삭제
중간 요소를 삭제하는 경우, 중간 요소를 삭제하고 -> 이후의 값을 앞당긴 다음 -> 마지막 값을 지워준다.
1
2
3
4
5
6
7
8
| // copy sol 1.
for i := idx+1; i<len(slice); i++ {
slice[i-1] = slice[i]
}
slice = slice[:len(slice)-1]
// copy sol 2.
slice = append(slice[:idx], slice[idx+1:]...) // 각 요소들 append
|
슬라이스 요소 추가
중간 요소를 추가하는 경우, 슬라이스 맨 뒤에 요소를 추가한다 -> 맨 뒷값부터 삽입하려는 위치까지 한칸씩 뒤로 밀어준다 -> 중간 요소부분과 값을 바꿔준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| // copy sol 1.
slice = append(slice, 0)
for i := len(slice)-2; i >= idx; i-- {
slice[i+1] = slice[i]
}
slice[idx] = 100
// copy sol 2.
slice = append(slice[:idx], append([]int{100}, slice[idx:]...)...)
// copy sol 2* 불필요한 메모리 개선하기
copy(slice[idx+1:], slice[idx:])
slice[idx] = 100
|
슬라이스 정렬
Go 기본패키지 -> sort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // sort 1.
// sort.Ints()
slice := []int{5, 2, 6, 3, 1, 4}
sort.Ints(slice)
// sort 2.
// sort.Sort() -> Len, Less, Swap 함수에 대한 구현이 필요
type Student struct {
Name string
Age int
}
type Students []Student
func (s Students) Len() int { return len(s) }
func (s Students) Less(i, j int) bool { return s[i].Age < s[j].Age }
func (s Students) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func main() {
s := []Student{ {"화랑", 31}, {"백두산", 52}, {"류", 42}, {"켄", 38}, {"송하나", 23} }
sort.Sort(Students(s))
fmt.Println(s)
}
|
Questions
Q1. 동적배열을 초기화하는 방법 2가지를 말하라
1
2
3
4
| // 1
slice := make([]int, x)
// 2
slice := []int{a,c,v,...}
|
Q2. slice에서 append()함수를 사용할 때 값을 추가할 빈 공간은 어떻게 계산하는가?
또 빈 공간이 없다면 append()는 어떻게 동작하는가?
1
2
| 1) cap - len > 값의 개수 로 비교
2) 기존 배열의 cap * 2로 cap을 늘인 새로운 배열을 지정하고 값을 추가
|
Q3. Slice 내부 헤더는 어떻게 구성되어 있는가(구성 3요소를 말하라)?
Q4. slice1 := []int{1, 2, 3, 4, 5} 에서 slice2 ( -> [1, 2] ) 를 추출하라. 이때 cap 사이즈는 3으로 설계하라.
1
| slice2 := slice1[0:2:3]
|
Q5. 위의 slice1의 인덱스 0과 1 사이에 200을 추가로 삽입하는 코드를 작성하라. idx = 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
slice1 := []int{1, 2, 3, 4, 5}
fmt.Println(slice1)
slice1 = append(slice1, 0)
idx := 1
for i := len(slice1) - 2; i >= idx; i-- {
slice1[i+1] = slice1[i]
}
slice1[idx] = 200
fmt.Println("After ---------------")
fmt.Println(slice1)
|
Memo
- slice = append(slice[:idx], append([]int{200}, slice[idx:]…)…)
- copy(slice[idx+1:], slice[idx:]) slice[idx] = 200