Slice in Go로 작업할 때 주의하세요.
26308 단어 programminggo
array
및 slice
데이터 구조가 있습니다. 둘 다 동일한 유형의 시퀀스 요소를 저장하는 데 사용됩니다. 차이점은 배열은 길이가 고정되어 있고 슬라이스는 길이가 가변적이라는 것입니다.어레이와 슬라이스 모두 길이와 용량이 있습니다. 길이는
len
, 용량은 cap
키워드로 확인할 수 있습니다.배열에서는 길이가 고정되어 있으므로 새 요소를 추가(길이 변경)할 수 없습니다. 반면 최대 슬라이스 용량에서도 슬라이스에 요소를 추가할 수 있습니다.
package main
import "fmt"
func main() {
length := 0
capacity := 3
x := make([]int, length, capacity)
// slice: [], length: 0, capacity: 3
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
}
위의 코드에서 길이가 0이고 용량이 3인 슬라이스를 만듭니다. 세 개의 요소를 추가하고 길이와 용량을 살펴보겠습니다.
package main
import "fmt"
func main() {
length := 0
capacity := 3
x := make([]int, length, capacity)
// slice: [], length: 0, capacity: 3
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
x = append(x, 1, 2, 3)
// slice: [1 2 3], length: 3, capacity: 3
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
}
여태까지는 그런대로 잘됐다. 이제
x
에 새 요소를 추가하고 어떻게 되는지 살펴보겠습니다.package main
import "fmt"
func main() {
length := 0
capacity := 3
x := make([]int, length, capacity)
// slice: [], length: 0, capacity: 3
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
x = append(x, 1, 2, 3)
// slice: [1 2 3], length: 3, capacity: 3
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
x = append(x, 4)
// slice: [1 2 3 4], length: 4, capacity: 6
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
}
보시다시피 길이는 4가 되고 용량은 6이 됩니다. 슬라이스가 최대 용량에 도달했을 때 새 요소를 추가하면 용량이 두 배가 됩니다.
그러나 더 흥미로운 점은 슬라이스가 용량을 초과하면 슬라이스가 새 메모리를 할당한다는 것입니다. 따라서 메모리에는 용량이 3인 슬라이스
[1 2 3]
와 용량이 6인 슬라이스[1 2 3 4]
가 있습니다. 변수x
는 새 어레이를 가리킵니다.이것은 우리가 할 때 메모리에서 일어나는 일입니다
x = append(x, 1, 2, 3)
.속성 길이와 용량 값이 각각 3과 3인 변수
x
가 있습니다. 새 요소( x = append(x, 4)
)를 추가하면 Go는 요소의 새 시퀀스를 만들고 이전 배열의 모든 값을 복사합니다.이전 배열이 더 이상 참조되지 않으며 나중에 Go Garbage Collector에서 해제됨을 알 수 있습니다.
슬라이스가 Go에서 작동하는 방식을 이해했습니다. 아래 문제를 봅시다.
package main
import "fmt"
func main() {
x := make([]int, 3, 5)
x = append(x, 1)
y := append(x, 2)
z := append(x, 3)
fmt.Printf("slice x: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
fmt.Printf("slice y: %v, length: %d, capacity: %d\n", y, len(y), cap(y))
fmt.Printf("slice z: %v, length: %d, capacity: %d\n", z, len(z), cap(z))
}
당신은 출력을 추측할 수 있습니까?
코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
# The first three element is initialized with 0 because we set the slice to have length 3.
slice x: [0 0 0 1], length: 4, capacity: 5
slice y: [0 0 0 1 3], length: 5, capacity: 5
slice z: [0 0 0 1 3], length: 5, capacity: 5
y
가 [0 0 0 1 2]
와 같지 않아야 합니까?이런 식으로 생각하고 있다면 실제로 무슨 일이 일어나고 있는지 이해하기 위해 메모리 표현을 단계별로 살펴보겠습니다.
package main
import "fmt"
func main() {
x := make([]int, 3, 5)
x = append(x, 1)
fmt.Printf("slice x: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
}
이때 메모리 표현은 아래와 같습니다.
그런 다음
x
에 2를 추가하고 변수y
에 할당합니다.x
와 y
가 동일한 배열을 가리키는 것을 볼 수 있습니다. 차이점은 y
의 길이가 5이고 x
의 길이가 4라는 것입니다. 현재 배열이 용량을 초과하지 않았기 때문에 Go는 새 배열을 만들지 않습니다.다음으로
x
에 3을 추가하고 z
에 할당합니다.x
를 추가하기 전에 x
의 길이는 4이고 용량은 5입니다. 따라서 z := append(x, 3)
를 수행할 때 Go는 2를 3으로 덮어씁니다. 아직 초과했습니다. 따라서 새 어레이가 할당되지 않습니다. 따라서 x
도 z
및 x
와 동일한 헤더를 가리킵니다.이를 증명하려면 이 코드를 실행해 보십시오.
package main
import "fmt"
func main() {
x := make([]int, 3, 5)
x = append(x, 1)
y := append(x, 2)
z := append(x, 3)
fmt.Printf("slice x: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
fmt.Printf("slice y: %v, length: %d, capacity: %d\n", y, len(y), cap(y))
fmt.Printf("slice z: %v, length: %d, capacity: %d\n", z, len(z), cap(z))
// change the value of third element of slice x
x[2] = 100
fmt.Printf("slice x: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
fmt.Printf("slice y: %v, length: %d, capacity: %d\n", y, len(y), cap(y))
fmt.Printf("slice z: %v, length: %d, capacity: %d\n", z, len(z), cap(z))
}
y
, x
및 y
의 세 번째 요소가 100인 것을 볼 수 있습니다. 이것은 이 세 포인터가 배열의 동일한 헤더를 가리키고 있음을 증명합니다.slice: [0 0 100 1], length: 4, capacity: 5
slice: [0 0 100 1 3], length: 5, capacity: 5
slice: [0 0 100 1 3], length: 5, capacity: 5
이제 아래와 같이 새 요소를 추가하면
z
어떻게 됩니까?package main
import "fmt"
func main() {
x := make([]int, 3, 5)
x = append(x, 1)
y := append(x, 2)
z := append(x, 3)
w := append(z, 4)
fmt.Printf("slice x: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
fmt.Printf("slice y: %v, length: %d, capacity: %d\n", y, len(y), cap(y))
fmt.Printf("slice z: %v, length: %d, capacity: %d\n", z, len(z), cap(z))
fmt.Printf("slice w: %v, length: %d, capacity: %d\n", w, len(w), cap(w))
}
이전에 논의한 것처럼
z
가 최대 용량에 도달했기 때문에 새 요소를 추가하면 Go가 새 배열을 만들고 z
가 해당 배열을 가리킵니다. w
의 용량은 w
보다 두 배 더 큽니다. 아래는 출력입니다.slice x: [0 0 0 1], length: 4, capacity: 5
slice y: [0 0 0 1 3], length: 5, capacity: 5
slice z: [0 0 0 1 3], length: 5, capacity: 5
slice w: [0 0 0 1 3 4], length: 6, capacity: 10
z
가 다른 배열을 가리키고 있음을 증명하기 위해 세 번째 요소의 값을 다시 변경해 보겠습니다.package main
import "fmt"
func main() {
x := make([]int, 3, 5)
x = append(x, 1)
y := append(x, 2)
z := append(x, 3)
w := append(z, 4)
x[2] = 100
fmt.Printf("slice: %v, length: %d, capacity: %d\n", x, len(x), cap(x))
fmt.Printf("slice: %v, length: %d, capacity: %d\n", y, len(y), cap(y))
fmt.Printf("slice: %v, length: %d, capacity: %d\n", z, len(z), cap(z))
fmt.Printf("slice: %v, length: %d, capacity: %d\n", w, len(w), cap(w))
}
아래는 출력입니다.
slice: [0 0 100 1], length: 4, capacity: 5
slice: [0 0 100 1 3], length: 5, capacity: 5
slice: [0 0 100 1 3], length: 5, capacity: 5
slice: [0 0 0 1 3 4], length: 6, capacity: 10
w
의 세 번째 요소는 여전히 100이 아니라 1임을 알 수 있습니다. 이것은 w
가 다른 배열을 가리키고 있음을 증명합니다.따라서 다음에는 Go 슬라이스로 작업할 때 주의하십시오.
Reference
이 문제에 관하여(Slice in Go로 작업할 때 주의하세요.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/uulwake/be-careful-when-working-with-slice-in-go-4ed3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)