-
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