Golang

[Golang] 슬라이스

GenieLove! 2022. 3. 4. 00:18
728x90
반응형

1.선언 및 초기화 방법

var slice []int
var slice1 []int{1, 2, 3}
var slice2 []int{1, 5:2, 10:3}//[1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3]//5인덱스의 값 2, 10인덱스의 값 3으로 할당

//var array [...]int{1, 2, 3}//배열은 앞에 ...

var slice3 = make([]int, 3)//길이 3짜리 int 슬라이스//[0, 0, 0]

2.append()

var slice = []int{1, 2, 3}
slice2 := append(slice, 4)
slice[2] = 6
fmt.Println(slice)//[1 2 6]
fmt.Println(slice2)//[1 2 6 4]


slice = append(slice, 4, 5, 6)
fmt.Println(slice)//[1 2 6 4 5 6]

3.슬라이스 내부 구현 - 슬라이스는 실제 배열을 가리키는 포인터를 가지고 있어, 쉽게 크기가 다른 배열을 가리키도록 변경 가능, 변수 대입 시 배열에 비해 사용되는 메모리, 속도에 이점이 있다.

type SliceHeader struct {
	Data	uintptr//실제 배열을 가리키는 포인터
    Len		int//요소 개수
    Cap		int//실제 배열 길이
}
var slice = make([]int, 2)// len 2, cap 2 -> 배열길이 2, 요소 개수 2
var slice2 = make)[]int, 2, 4)//배열길이 2 요소 개수 4 -> 나머지 2개는 나중에 추가될 요소를 위해 비워둠

4.함수 파라미터 전달 시 문제 - 슬라이스는 포인터로 접근하기 때문에 파라미터로 전달하여 값을 변경 시 기존에 있던 슬라이스 값도 같이 변경된다.(배열은 배열의 모든 값 복사, 슬라이스는 포인터도 복사된다)

func change(slice []int) {
	slice[1] = 5
}

func main() {
	s := []int{1, 2, 3}
   	change(s)
    fmt.Println(s)//[1 5 3]
}

5.남은 공간에 따른 append차이

남은 공간 = cap - len

slice1 := make([]int, 3, 5)
slice2 := append(slice1, 6, 7)
//slice1 - len=3, cap=5
//slice2 - len=5, cap=5
//slice1, slice2 둘 다 같은 주소를 바라본다

slice1[1] = 20
fmt.Println(slice1)//[0 20 0]
fmt.Println(slice2)//[0 20 0 6 7]

slice1 = append(slice1, 9)
fmt.Println(slice1)//[0 20 0 9]
fmt.Println(slice2)//[0 20 0 9 7]//slice2값까지 바뀐다.
slice1 := []int{1, 2, 3}//len=3, cap=3
//남은 공간이 충분하지 않으면 기존 배열 크기의 2배로 만들고 기존 배열 값을 새로운 배열 값에 복사
slice2 := append(slice1, 4, 5)//len=5, cap=6 <- 2배로 복사돼서 cap이 3*2값이 됨

slice1[1] = 4
fmt.Println(slice1)//[1 4 3]
fmt.Println(slice2)//[1 2 3 4 5]//서로 다른 주소를 갖기 때문에 값 변경 X

6.슬라이싱 - 배열의 일부를 집어내는 기능(포인터로 접근되어 기존 배열(혹은 슬라이스)와 동일한 주소를 바라봐서 값이 같이 변경된다.)

slice[startIndex:endIndex]//시작 인덱스 ~ 끝 인덱스 - 1까지의 배열 일부


array := [5]int{1, 2, 3, 4, 5}

slice := array[1:3]

fmt.Println(array)//[1 2 3 4 5]
fmt.Println(slice)//[2 3]

slice[0] = 6

fmt.Println(array)//[1 6 3 4 5]
fmt.Println(slice)//[6 3]

slice = append(slice, 7)

fmt.Println(array)//[1 6 3 7 4 5]
fmt.Println(slice)//[6 3 7]

7.슬라이싱 방법

slice[시작인덱스:끝인덱스:최대인덱스]//cap 크기 조절 - cap = 최대인덱스 - 시작 인덱스
									//원래는 cap = 끝인덱스 - 시작인덱스

8.독립적인 슬라이스 복제

slice1 := []int{1, 2, 3}
//첫번째 방법
slice2 := make([]int, len(slice1))

for i, v :=  range slice1 {
	slice2[i] = v
}



//두번째 방법
slice2 := append([]int{}, slice1)
//slice2 := append([]int{}, slice1[0], slice1[1], slice1[2])과 같음


//세번째 방법 - copy이용
slice2 := make([]int, 2, 4)//len:2, cap:4
slice3 := make([]int, 5)//len:5, cap:5

//count2는 복사된 요소 개수
//copy(복사될 대상, 복사할 대상)
count2 := copy(slice2, slice1)//slice2의 요소개수 = 2, slice1의 요소개수 = 3 -> 둘 중 작은 값인 2개 복사//[1 2]
count3 := copy(slice3, slice1)//slice3의 요소개수가 더 많으므로 전체 복사//[1 2 3 0 0]

9.요소 삭제

slice := []int {1, 2, 3, 4}
index := 2


//첫번째 방법
for i := index + 1; i < len(slice); i++ {
	slice[i - 1] = slice[i]
}
slice = slice[:len(slice)-1]
fmt.Println(slice)//[1 2 4]


//두번째 방법
slice = append(slice[:index], slice[index+1:]...)

10.요소추가

//slice[index+1:] = [3, 4 5]
//첫번째 방법 - [100, 3, 4 ,5]불필요 메모리 사용
slice = append(slice[:index], append({}int{100}, slice[index+1:]...)...)


//두번째 방법
slice = append(slice, 0)//맨 뒤에 요소 추가
copy(slice[index+1:], slice[:index])
slice[index] = 100

11.슬라이스 정렬

(1)int

slice := []int {5, 4, 3, 1, 2}
sort.Ints(slice)//Float64s()로 float64 슬라이스 정렬 가능
fmt.Println(slice)//[1 2 3 4 5]

(2)구조체

type User struct {
	Name	string
    Age		int
}

//첫번째 방법 - 정렬 인터페이스 이용
type Users []User

func (u Users) Len() int {
	return len(u)
}

func (u Users) Less(i, j int) bool {
	return u[i].Age < u[j].Age
}

func (u User) Swap(i, j int) {
	u[i], u[j] = u[j], u[i]
}


users := []User{
	{"genie", 5}, {"babo", 40}, {"soomin", 28}
}
sort.Sort(Users(users))
//[{genie 5} {soomin 28} {babo 40}]



//두번째 방법 - 사용자 정의 Comparator이용
users := []User{
	{"genie", 5}, {"babo", 40}, {"soomin", 28}
}
//sort.Slice()함수도 사용가능
sort.SliceStable(users, func(i, j int) bool {//true리턴 시 변경 X, false리턴 시 변경
	return users[i].Age < users[j].Age
}
728x90
반응형