Preface
Go does not provide syntax or function for deleting slice elements, and needs to use the characteristics of the slice itself to delete elements.
There are generally the following methods to delete the specified elements of slices. This article uses []int as an example to give a specific implementation.
1. Intercept method (modify the original slice)
Here, the specified element is deleted by intercepting the slice. Note that when deleting, the subsequent elements will move forward, so the subscript i should be moved one by one left.
// DeleteSlice1 Deletes the specified element.func DeleteSlice1(a []int, elem int) []int { for i := 0; i < len(a); i++ { if a[i] == elem { a = append(a[:i], a[i+1:]...) i-- } } return a }
2. Copy method (no change the original slice)
This method is easiest to understand, reuse a slice and filter out the elements to be deleted. The disadvantage is that it needs to open up another slice space, the advantage is that it is easy to understand and will not modify the original slice.
// DeleteSlice2 Deletes the specified element.func DeleteSlice2(a []int, elem int) []int { tmp := make([]int, 0, len(a)) for _, v := range a { if v != elem { tmp = append(tmp, v) } } return tmp }
3. Shift method (modify the original slice)
3.1 Method 1
Use a index to record the location where the next valid element should be. Iterate through all elements, when a valid element is encountered, move it to index and index is added by one. The final index position is the next position of all valid elements, and you can do an intercept. This method will modify the original slice.
This method can be seen as an improvement to the interception method of the first method, because each point needs to move one element, the performance is even better.
// DeleteSlice3 Deletes the specified element.func DeleteSlice3(a []int, elem int) []int { j := 0 for _, v := range a { if v != elem { a[j] = v j++ } } return a[:j] }
3.2 Method 2
A slice is created, but the underlying array of the original slice is shared. This does not require additional memory space to be allocated, and it is directly modified on the original slice.
// DeleteSlice4 Deletes the specified element.func DeleteSlice4(a []int, elem int) []int { tgt := a[:0] for _, v := range a { if v != elem { tgt = append(tgt, v) } } return tgt }
4. Performance comparison
Suppose our slice has 0 and 1, we want to delete all 0s.
Here we test slices with lengths of 10, 100, and 1000 to achieve the performance differences in the above four implementations.
The generated slice function is as follows:
func getSlice(n int) []int { a := make([]int, 0, n) for i := 0; i < n; i++ { if i%2 == 0 { a = append(a, 0) continue } a = append(a, 1) } return a }
The benchmark code is as follows:
func BenchmarkDeleteSlice1(b *) { for i := 0; i < ; i++ { _ = DeleteSlice1(getSlice(10), 0) } } func BenchmarkDeleteSlice2(b *) { for i := 0; i < ; i++ { _ = DeleteSlice2(getSlice(10), 0) } } func BenchmarkDeleteSlice3(b *) { for i := 0; i < ; i++ { _ = DeleteSlice3(getSlice(10), 0) } } func BenchmarkDeleteSlice4(b *) { for i := 0; i < ; i++ { _ = DeleteSlice4(getSlice(10), 0) } }
The test results are as follows:
The original slice length is 10:
go test -bench=. main/slice
goos: windows
goarch: amd64
pkg: main/slice
cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
BenchmarkDeleteSlice1-8 17466486 65.07 ns/op
BenchmarkDeleteSlice2-8 14897282 85.22 ns/op
BenchmarkDeleteSlice3-8 21952129 50.78 ns/op
BenchmarkDeleteSlice4-8 22176390 54.68 ns/op
PASS
ok main/slice 5.427s
The original slice length is 100:
BenchmarkDeleteSlice1-8 1652146 762.1 ns/op
BenchmarkDeleteSlice2-8 2124237 578.4 ns/op
BenchmarkDeleteSlice3-8 3161318 359.9 ns/op
BenchmarkDeleteSlice4-8 2714158 423.7 ns/op
The original slice length is 1000:
BenchmarkDeleteSlice1-8 56067 21915 ns/op
BenchmarkDeleteSlice2-8 258662 5007 ns/op
BenchmarkDeleteSlice3-8 432049 2724 ns/op
BenchmarkDeleteSlice4-8 325194 3615 ns/op
5. Summary
Judging from the benchmark test results, the best performance method is the shift method, and the first implementation method is the better. The worst and most commonly used method is interception. As the slice length increases, the performance differences between the above four deletion methods will become more obvious.
When actually using it, we can choose based on the scenarios that are not used. If the original slice usage copy method cannot be modified, the first implementation method of the original slice usage shift method can be modified.
References
- golang deletes elements of specific conditions in slice, optimized version
- 【Golang】Slice performance comparison of deleted elements
This is the article about the comparison of three methods of deleting specified elements in Golang slices. For more related contents of deleting specified elements in Golang slices, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!