Home 1. slice
Post
Cancel

1. slice

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 동작 원리

reflect package > SliceHeader

1
2
3
4
5
6
7
8
  // SliceHeader의 모습
  type SliceHeader struct {
    Data uintptr // 배열 포인터
    Len  int // 요소 개수
    Cap  int // 배열 길이
  }
  // 슬라이스 변수 대입 시 배열에 비해 사용되는 메모리나 속도에 이점이 있다.
  // 아래 동작 차이 참조

Slice 와 Array 동작차이

SliceArray 는 동작 방식에 차이가 있다. 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요소를 말하라)?

1
  ptr, len, cap

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

    1. slice = append(slice[:idx], append([]int{200}, slice[idx:]…)…)
    2. copy(slice[idx+1:], slice[idx:]) slice[idx] = 200
This post is licensed under CC BY 4.0 by the author.