배열, 슬라이스 모두 연속된 메모리공간을 순차적으로 이용하는 자료구조
배열은 메모리 공간 그자체지만, 슬라이스는 배열을 가리키는 구조체이다.
fruits := make([]string, 3, 5)
fruits[0] = "사과"
fruits[1] = "바나나"
fruits[2] = "토마토"
슬라이스에 용량이라는 개념이 있는 이유는 배열은 연속된 메모리공간을 쓰기때문에 내용을 추가하고 싶을 때
사용하던 메모리공간 뒤쪽이 이미 사용중이라면 원하는 크기의 다른 메모리공간을 찾아야 새로 배열을 만들어야하기 때문에 효율이 좋지않기 때문이다.
위에서처럼 배열은 [...]string{"사과", "바나나", "토마토"}
처럼 미리 자료형과 크기를 지정하기 때문에 append를 못하고 유연하지 못하다.
슬라이스는 포인터로 새로운 배열을 가리키면 되기 때문에 배열 크기가 가변적이다
append를 이용해 기존 슬라이스에 덧붙이는 방법은 fruits = append(fruits, "포도")
처럼
기존 슬라이스 = append(기존 슬라이스, 추가하고 싶은 값, 추가하고싶은 값, ...) 으로 쓰면 된다.
func Example_sliceCopy() {
src := []int{30, 20, 50, 10, 40}
dest := make([]int, len(src))
for i, slice = range src {
dest[i] = src[i]
}
fmt.Println(dest)
// Output: [30 20 50 10 40]
}
위와 같이 for문을 이용하는 방법말고
copy함수를 이용한 방법이 있다
copy(dest, src)
를 이용하면 복사가 되지만
len(src), len(dest)중 더 작은 길이만큼만 복사한다
golang 삽입/삭제 메서드는 따로 없기 때문에 몇가지 패턴을 이용해야한다.
첫번 째로 append를 이용한 패턴이다.
a = append(a[:i+1], a[i:]...)
a[i] = x
if i < len(a) {
a = append(a[:i+1], a[i:]...)
a[i] = x
} else {
a = append(a, x)
}
이는
a = append(a, x) 중간에 삽입하는 경우 외에 맨 오른쪽에 추가하는 경우도 고려한 코드이다.
두번 째로 copy를 이용한 다른 패턴을 보면
a = append(a, x)
copy(a[i+1:], a[i:])
a[i] = x
여러개를 삽입할 때는
a = append(a, x...)
copy(a[i+len(x):], a[i:])
copy(a[i:], x)
copy(a[i+1:], a[i:])
에서 +1이 아닌 +len(x)을 한 점이 다르다
삭제의 경우
a = append(a[:i], a[i+1:]...)
여러개를 삭제하고 싶다면
a = append(a[:i], a[i+k:]...)
앞에서처럼 +1대신 +k를 하면된다.
만약 순서가 바뀌어도 된다면 O(n)의 시간복잡도를 가진 앞의 방법들과 다르게 O(1)만 걸리는 패턴이 있다.
a[i] = a[len(a)-1]
a = a[:len(a)-1]
위 방법으로 여러개를 지우는 경우는
start := len(a) - k
if i + k > start {
start = i + k
}
copy(a[i:i+k], a[start:])
a = a[:len(a) - k]
메모리 문제를 해결한 코드는 아래와 같이 삭제하는 값들을 nil로 바꾼다
copy(a[i:], a[i+k:])
for i := 0; i < k; i++ {
a[len(a)-1-i] = nil
}
a = a[:len(a)-k]