ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Go 슬라이스
    @ 17. 1 ~ 18/Go 랭 2020. 7. 4. 13:23

    자료구조

    배열과 슬라이스

    슬라이스는 연속된 메모리 공간. 순차적으로 이용하는 자료구조

    배열이 직접사용되는 경우도 있지만 주로 슬라이스를 이요하여 간접적으로 배열을 이용하는 경우가 많다

     

    1. 배열

    func Exampe_Array(){
    	fruits :=[3]string{"사과", "바나나", "토마토"}
        for _, fruit := range fruits{
        	fmt.Println("%s는 맛있다", fruit)
        }
        
        //output
        //사과는 맛있다
        //바나나는 맛있다
        //토마토는 맛있다
    }

    range 키워드에서 첫번째 제어변수는 0,1,2... 인덱스 두번쨰 제어변수는 각각의 값들이 순서대로 받아진다

    컴파일러가 배열의 수를 알아내어서 넣게 만들고 싶다면

    fruits := [...]string{"사과", "바나나", "토마토"} 이렇게 해도 된다

     

    2. 슬라이스

    배열보다 정말 유연하게 사용할 수 있다

    위에 [3]string 보다 []string이여도 잘 작동한다.

    배열은 크기가 자료형에 고정되어있다 즉, [3]string 자료형은 3개의 문자열로 구성된 배열 자료형이다

    슬라이스는 길이와 용량을 갖고 있고 길이가 변할 수 있는 구조이다

    var fruits = []string //빈 문자열 슬라이드

    빈 슬라이스에는 nil 이 들어간다 null 이랑 같은 개념이다

    빈 스트링을 n개 갖고 있는 슬라이스는?

    fruits := make([]string, n)

    make로 만드렉 되면 만든 슬라이스에는 자료형의 기본값이 들어가게 된다

    nil은 포인터형과 슬라이스를 비롯하여 맵, 인터페이스 등의 기본값으로 들어가게 된다

    이름 처럼 자를 수 있다

    func Example_sliceing(){
    	nums := []int{1,2,3,4,5}
        fmt.Println(nums)
        fmt.Println(nums[1:3])
        fmt.Println(nums[2:])
        fmt.Println(nums[:3])
        
        //output
        //1,2,3,4,5
        //2,3
        //3,4,5
        //1,2,3
    }

    앞의 숫자는 시작 인덱스를 이야기하면서  <= 조건이고 뒤에 숫자는 끝 인덱스하면서 < 이다

    숫자는 음수가 되지 않는다 nums[:-1]

     

    슬라이스 덧붙이기

    fruits = append(fruits, "포도")

    fruits가 두번 쓰였다. 

    fuits = append(fuits, "포도", "딸기")

    덧붙이기를 여러개 할 수도 있다 append함수는 가변 인자를 받거든요

    func Example_append(){
    
    	f1 := []string{"사과", "바나나", "토마토"}
        f2 := []string{"포도", "딸기"}
        f3 := append(f1, f2...)			//이어 붙이기
        f4 := append(f1[:2], f2...)		//토마토를 제외하고 이어 붙이기
    
    	fmt.Println(f1)
        fmt.Println(f2)
        fmt.Println(f3)
        fmt.Println(f4)
        
        //output
        //[사과 바나나 토마토]
        //[포도 딸기]
        //[사과 바나나 토마토 포도 딸기]
        //[사과 바나나 포도 딸기]
    }

    f3 := append(f1, f2)와 같이 썼다면 f2는 문자열이 아니고 문자열 슬라이스이기 때문에 f1뒤에 추가할 수 없다

    append(f1, f2...)

    append했을때(슬라이스 덧붙이기) 기존의 슬라이스가 공간이 없다면 용량이 부족하기 때문에 

    슬라이스 전체를 복사하게 됩니다(다른 더 넓은 곳으로 복사를 한 뒤에야 덧 붙일 수 있기 떄문이다)

    슬라이스에서 비록 잘라냈더라도 뒤에 공간이 있으면 그 공간을 살릴 수도 있다

    func Example_sliceCap() {
    	nums := []int{1, 2, 3, 4, 5}
    	fmt.Println(nums)
    	fmt.Println("len:", len(nums))
    	fmt.Println("cap:", cap(nums))
    	fmt.Println()
    
    	slice1 := nums[:3]
    	fmt.Println(slice1)
    	fmt.Println("len:", len(slice1))
    	fmt.Println("cap:", cap(slice1))
    	fmt.Println()
    
    	slice2 := nums[2:]
    	fmt.Println(slice2)
    	fmt.Println("len:", len(slice2))
    	fmt.Println("cap:", cap(slice2))
    	fmt.Println()
    
    	slice3 := slice1[:4]
    	fmt.Println(slice3)
    	fmt.Println("len:", len(slice3))
    	fmt.Println("cap:", cap(slice3))
    	fmt.Println()
    
    	nums[2] = 100
    	fmt.Println(nums, slice1, slice2, slice3)
    }
    
    
    //output
    [1 2 3 4 5]
    len: 5
    cap: 5
    
    [1 2 3]
    len: 3
    cap: 5
    
    [3 4 5]
    len: 3
    cap: 3
    
    [1 2 3 4]
    len: 4
    cap: 5
    
    [1 2 100 4 5] [1 2 100] [100 4 5] [1 2 100 4]

    잘려진 슬라이스들은 모두 동일한 메모리를 보고 있는 것이다

    nums := make([]int, 3, 5) //길이는 3 용량은 5로 슬라이스 만듭니다

    위와 아래는 같습니다

    nums := make([]int, 5)

    nums = nums[:3]

     

    빈 슬라이스를 생성하고 싶지만, 미리 공간을 예약해두고 싶을 떄가 있습니다. 즉, N개까지 길이가 늘어나더라도

    복사가 일어나지 않게 하고 싶은 경우입니다

    nums := make([]int, 0, 15)

    append시 15개를 넘어가지않으면 복사가 일어나지 않습니다

     

    3. 내부 구현

    슬라이스는 3개의 필드들로 이루어져있다

    시작주소, 길이, 그리고 용량

    시작주소로 여러 슬라이스가 동일한 배열을 공유할 수 있는것이다

    복사가 일어나서 이동이 일어났다고 했을때에는 그 슬라이스는 이제 다른 배열을 보고 있게 된다

    배열은 크기가 변경될 수 없기 때문에 크기가 다른 배열을 하나 더 만들어야 한다

     

    이제 왜 append에서 두번 반복해서 써주어야 하는지 이유가 이것이다

    nums = append(nums, 10) //결과값 받는것, append 인자로 넣는것 => 두번 반복해서 쓴다

     

    3-1) 슬라이스 복사

    func Example_sliceCopy() {
    	src := []int{30, 20, 50, 10, 40}
    	dest := make([]int, len(src))
    	for i := range src {
    		dest[i] = src[i]
    	}
    
    	fmt.Println(dest)
    }
    
    
    //output
    [30 20 50 10 40]
    

    range 에서 하나로 받을때(i) 인덱스가 들어갑니다. 값이 아니라 인덱스.. 0,1,2,3,4

    위에보다 간편한건 copy(dest, src)이다

    len(src)과 len(dest)중에서 더 작은 값만큼만 복사합니다. copy는 실제 몇개가 복사되었는지 반환합니다

     

    if n:= copy(dest, src); n != len(src){

    }

    if안에 조건은 세미콜론을 써서 구분을 할 수 있다 n은 if문안에서 생성된것이므로 if ~ else 밖으로 나가면

    소멸되어 접근할 수가 없습니다

     

    얕은복사

    dest := src

    깊은복사

    dest := append([]int(nil), src...) 또는 copy(dest, src)

     

     

    '@ 17. 1 ~ 18 > Go 랭' 카테고리의 다른 글

    GO RWMutex vs Mutex  (0) 2020.07.16
    GO 문법 정리(진행형)  (0) 2020.07.07
    GO 가변인자  (0) 2020.07.04
Designed by Tistory.