golang 슬라이스 Slices

3 minute read

1) 슬라이스 slices

  • 배열과 비슷하지만, 길이가 고정되어 있는 배열과 달리 동적으로 크기를 변경하여 사용할 수 있다

    • 배열을 내장하고 있음
  • []type : 배열과 달리 [] 안에 숫자나 … 넣지 않음!!

    // 방법 1 : 빈 슬라이스 (슬라이스 길이 0)
    var a []int
    // 방법 2 : 생성과 동시에 초기화 가능
    b := []int{1,2,3,4}
    // 방법 3
    c := make([]int, 5)
    
    • 빈 슬라이스는 nil 값을 가짐 []
      • nil 은 null 과 비슷한 개념!
    • nil 슬라이스는 길이의 최대 크기가 0

make() : 값을 넣을 공간 할당

  • 슬라이스에 값을 넣기 위해서는 make() 를 사용하여 공간을 할당해 주어야 함

    • 만약 그냥 빈 슬라이스를 선언하면 공간이 없어서 값을 못 넣어 ( 빈 슬라이스 a에 a[0] = 2 하면 에러)
    • 대신 다른 슬라이스를 대입해서 슬라이스 참조는 가능
  • 슬라이스의 요소는 모두 0으로 초기화 된다

    // 방법 1
    var a []int = make([]int, 3)
    // 방법 2
    var b = make([]int, 3)
    // 방법 3
    a := make([]int, 3)
    
    • 모두 [0 0 0]

make( []자료형, 길이, 용량 )

  • 길이

    • 인덱스로 접근 가능한 최대 공간**

    • 용량이 더 확보되어 있더라도 길이 이상으로 접근하려고 하면 에러
    • len()
  • 용량
    • 요소가 추가되면 자동으로 늘어나는 공간
    • 실제 메모리에 할당된 공간
    • cap()
  • 용량 >= 길이

    • make() 에서 용량을 생략하면 자동으로 용량==길이
  • 용량을 처음부터 많이 할당하면 요소가 추가될 때마다 메모리를 일일이 할당하지 않아도 돼서 성능상 이점이 있음 (단, 처음부터 너무 메모리를 잡아먹어)

  • 용량을 작게 할당하면 요소가 추가될 때마다 메모리를 할당해줘야 해서 성능이 떨어질 수 있음 (단, 메모리 공간을 적게 차지)
a := make([]int, 3, 7) // [0 0 0]
len(a) // 3
cap(a) // 7
  • 잠재(?) 공간이 4개 더 있음! (용량 7인데 길이 3으로 공간 3개 사용했으니까 4개 더 남았다)
  • a 에 4개의 요소 값을 더 추가하면 공간이 이미 할당되어 있으니까 메모리 할당을 안 해줘도 돼
  • 이제 4개 초과로 값을 추가하면 더이상 할당된 공간이 없어서 용량을 늘려야 함 (메모리 추가 할당)

append() : 값 & 슬라이스 추가하기

  • 값을 추가

    a := []int{1,2,3}
    a = append(a,4,5,6)
    fmt.Println(a) // [1 2 3 4 5 6]
    
  • 슬라이스에 다른 슬라이스를 붙이기

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

2) 슬라이스는 레퍼런스 타입!

  • 슬라이스는 내장된 배열에 대한 포인터

  • 그래서 a 슬라이스에 b 슬라이스를 대입해도 값이 복사되는 것이 아니라 주소값이 복사 -> 참조

    ( 배열은 대입하면 값을 그대로 복사함)

  • 배열의 경우

    // 배열의 경우
    a := [2]int{1,2}
    var b [2]int
        
    b = a	// 배열 a의 값을 그대로 복사 [1 2]
        
    b[0] = 3	   
        
    fmt.Println(a) // [1 2]
    fmt.Println(b) // [3 2]
    
    • 배열 b는 배열 a의 값을 그대로 복사한 서로 다른 배열이기 때문에 b 요소의 값을 변경해도 a 에는 영향을 끼치지 않음
  • 슬라이스의 경우

    // 슬라이스의 경우
    a := []int{1,2}
    var b []int
        
    b = a
    b[0] = 7  // 주소에 있는 값을 변경
        
    fmt.Println(a) // [7 2]
    fmt.Println(b) // [7 2]
    
    • 서로 다른 슬라이스가 아니라 같은 주소값을 참조하기 때문에 하나의 값을 변경하면 둘 다 변경됨

copy(b, a) : b 에다가 a 값 복사

  • 슬라이스의 요소를 복사하고 싶을 때 사용한다

  • 값만 복사한 서로 다른 슬라이스 !

    a := []int{1,2,3}
    b := make([]int, 4) // [0 0 0 0]
    c := make([]int, 1) // [0]
      
    copy(b, a)
    copy(c, a)
      
    fmt.Println(a) // [1 2 3]
    fmt.Println(b) // [1 2 3 0]
    fmt.Println(c) // [1]
    
    • 이때 var b []int 같이 빈슬라이스를 선언하면 값을 못 넣음
    • c 의 길이는1 이라 a 의 값들 중 1개만 복사해올 수 있음
  • 서로 다른 슬라이스가 되기 때문에 요소값을 변경해도 다른 슬라이스에 영향을 끼치지 않음!

    a[0] = 100
      
    fmt.Println(a) // [100 2 3]
    fmt.Println(b) // [1 2 3 0]
    

3) 부분 슬라이스 [beg : end]

  • sliceA[beg : end]

    • sliceA의 인덱스 beg 부터 end-1 까지 참조한 슬라이스를 만든다

    • 슬라이스의 값을 직접 복사한게 아니라서 부분 슬라이스의 일부를 바꾸면 원본 슬라이스의 값도 바뀜 (슬라이스는 레퍼런스 타입!!)

      ( 배열이라도 부분 슬라이스를 할 경우 레퍼런스 타입처럼 취급 - 참조)

    • 참조가 싫으면 copy() 써서 부분 값을 복사해오면 돼

      • copy(b, a[0:3]) 값만 복사해와서 서로 영향 XX
  • 예시

    a := []int{1,2,3,4}
    b := a[1:3]  // a 슬라이스의 인덱스 1 2 값 참조
      
    fmt.Println(a) // [1 2 3 4]
    fmt.Println(b) // [2 3]
    
    b[0] = 100
      
    fmt.Println(a) // [1 100 3 4]
    fmt.Println(b) // [100 3]
    
    • 레퍼런스 타입이기 때문에 하나의 값을 변경하면 다른 슬라이스에도 영향을 미침
  • sliceB := sliceA[beg : end : capacity]

    • 부분 슬라이스 할 때 용량을 지정해 줄 수 있음
    • 단 원본 슬라이스의 용량을 넘으면 안돼!

더보기

Categories:

Updated:

Comments