Go语言Google开发的适用于多核编程的语言。我感觉它像是C语言的现代版本,简单,并发支持友好,部署轻松。GO语言中保留关键字就只有25个,这也足以说明它的学习成本并不高。
然而,Go语言里面slice这个东西并不简单。初学者容易掉入坑中。此文件就试图把slice给讲解清楚。
下面先讲一下slice的一些基本特性。
1. slice内部有三个变量,分别是:ptr, len, cap
ptr是用来存储数据的数组
cap是ptr数组的长度
len是实际数组的长度
2. 如何在初始化的时候,指定slice的长度?
a := make([]int, 10)
这个时候它的capacity是多少呢?
fmt.Println(cap(a))
这里打印出来是10。
也就是采用这种方式初始化出来的slice,它的capacity就是长度。
3. 如何在初始化的时候,指定slice的capacity?
a := make([]int, 0, 10)
我们在写代码的时候,如何事先能够预知一个slice的最大长度,就可以通过指定capacity的方式,优化我们的程序。毕竟,就多加了一个参数而已,我们就减少了几次内存分配释放的开销。
4. 如何往一个slice里面添加数据?添加好之后新的slice和老的有什么样的关系?
b := append(a, 1)
a = append(a, 1)
b := append(a, 1)
b和a有什么样的关系呢?
这个要看情况了。
如果len(a)+1<=cap(a),这个时候a内部的数组仍足够存储新添加的数据,此时,b的ptr和a的ptr是相同的。此时:b.ptr == a.ptr, len(b) == len(a)+1, cap(b) == cap(a)。
5. 如何对一个slice重新切片?重新切片之后的ptr, len, cap和原来的slice有何关系?
这里重新切片的意思,就是取slice里面的一部分元素。
a := make([]int, 10, 20)
b := a[0:5]
如上面这段代码,就是重新切片的。此时b.ptr == a.ptr。b.cap == a.cap。b.len == 5。
好了,了解了上面的基础属性,现在就可以开始练练手了。
1. 看看下面的代码会输出什么:
package main
func main() {
a := make([]int, 0)
b := append(a, 1)
_ = append(a, 2)
println(b[0])
}
package main
func main() {
a := make([]int, 0, 10)
b := append(a, 1)
_ = append(a, 2)
println(b[0])
}
执行完:
b := append(a, 1)
执行:
_ = append(a, 2)
2. 看看下面的代码会输出什么:
package main
func main() {
a := make([]int, 10, 20)
b := a[5:]
println(len(b), cap(b))
}
输出5是因为a的长度为10,b := a[5:],相当于是对a重新切片,取a第5个元素以后的值。a第5个元素之后还剩下5个值,那len(b)就是5了。
cap(b)为什么为15呢?
因为此时, b.ptr = a.ptr + 5。也就是b内部指针,指向了a.ptr的后面第5个元素。所以此时cap(b)就不能是20了,因为b无法利用a前面的5个元素。
3. 如果避免重新切片之后的新切片,不被修改?如下所示:
package main
import (
"fmt"
)
func doAppend(a []int) {
_ = append(a, 0)
}
func main() {
a := []int{1, 2, 3, 4, 5}
doAppend(a[0:2])
fmt.Println(a)
}
这段代码会输出:
[1 2 0 4 5]
虽然我们调用doAppend的时候,只把2个元素传入了。但它却把a的第3个元素改掉了。如何避免呢?答案如下:
package main
import (
"fmt"
)
func doAppend(a []int) {
_ = append(a, 0)
}
func main() {
a := []int{1, 2, 3, 4, 5}
doAppend(a[0:2:2])
fmt.Println(a)
}
doAppend(a[0:2:2])
4. 假设某个函数一定不会被多goroutine同时调用,如何优化函数内部的内存分配?
package main
import (
"fmt"
)
// 会被调用很多很多次的函数
func concat(a, b, c, d []byte) []byte {
r := make([]byte, len(a)+len(b)+len(c)+len(d))
r = append(r, a...)
r = append(r, b...)
r = append(r, c...)
r = append(r, d...)
return r
}
func main() {
for i := 0; i < 100; i++ {
fmt.Printf("%s\n", concat([]byte("1"), []byte("2"), []byte("3"), []byte("4")))
}
}
package main
import (
"fmt"
)
var cache = make([]byte, 0, 100)
// 会被调用很多很多次的函数
func concat(a, b, c, d []byte) []byte {
newLen := len(a)+len(b)+len(c)+len(d)
if newLen > cap(cache) {
cache = make([]byte, newLen*2)
}
r := cache[0:0]
r = append(r, a...)
r = append(r, b...)
r = append(r, c...)
r = append(r, d...)
return r
}
func main() {
for i := 0; i < 100; i++ {
fmt.Printf("%s\n", concat([]byte("1"), []byte("2"), []byte("3"), []byte("4")))
}
}
差不多了,先写这么多。如何错误敬请指正,还望对您有用。