append 函数

  • append 函数也是内置函数,它可以将元素添加到 slice 里面。
  1. package main
  2. import "fmt"
  3. func main() {
  4. dwarfs := []string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"}
  5. dwarfs = append(dwarfs, "Orcus")
  6. fmt.Println(dwarfs)
  7. dwarfs = append(dwarfs, "Salacia", "Quaoar", "Sedna")
  8. fmt.Println(dwarfs)
  9. }

长度和容量(length & capacity)

  • Slice 中元素的个数决定了 slice 的长度。
  • 如果 slice 的底层数组比 slice 还大,那么就说该 slice 还有容量可供增长。
  1. package main
  2. import "fmt"
  3. // dump slice length, capacity, and contents
  4. func dump(label string, slice []string) {
  5. fmt.Printf("%v: length %v, capacity %v %v\n", label, len(slice), cap(slice), slice)
  6. }
  7. func main() {
  8. dwarfs := []string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"}
  9. dump("dwarfs", dwarfs)
  10. dump("dwarfs[1:2]", dwarfs[1:2])
  11. }

详解 append

更大的slice - 图1

  1. package main
  2. import "fmt"
  3. func dump(label string, slice []string) {
  4. fmt.Printf("%v: length %v, capacity %v %v\n", label, len(slice), cap(slice), slice)
  5. }
  6. func main() {
  7. dwarfs1 := []string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"}
  8. dwarfs2 := append(dwarfs1, "Orcus")
  9. dwarfs3 := append(dwarfs2, "Salacia", "Quaoar", "Sedna")
  10. dump("dwarfs1", dwarfs1)
  11. dump("dwarfs2", dwarfs2)
  12. dump("dwarfs3", dwarfs3)
  13. dwarfs3[1] = "Pluto!"
  14. dump("dwarfs1", dwarfs1)
  15. dump("dwarfs2", dwarfs2)
  16. dump("dwarfs3", dwarfs3)
  17. }

三个索引的切分操作

  • Go 1.2 中引入了能够限制新建切片容量的三索引切分操作。
  1. package main
  2. import "fmt"
  3. // dump slice length, capacity, and contents
  4. func dump(label string, slice []string) {
  5. fmt.Printf("%v: length %v, capacity %v %v\n", label, len(slice), cap(slice), slice)
  6. }
  7. func main() {
  8. planets := []string{
  9. "Mercury", "Venus", "Earth", "Mars",
  10. "Jupiter", "Saturn", "Uranus", "Neptune",
  11. }
  12. terrestrial := planets[0:4:4]
  13. worlds := append(terrestrial, "Ceres")
  14. fmt.Println(planets)
  15. dump("terrestrial", terrestrial)
  16. terrestrial = planets[0:4]
  17. worlds = append(terrestrial, "Ceres")
  18. fmt.Println(planets)
  19. dump("terrestrial", terrestrial)
  20. _ = worlds
  21. }

使用 make 函数对 slice 进行预分配

  • 当 slice 的容量不足以执行 append 操作时,Go 必须创建新数组并复制旧数组中的内容。
  • 但通过内置的 make 函数,可以对 slice 进行预分配策略。
    • 尽量避免额外的内存分配和数组复制操作。
  1. package main
  2. import "fmt"
  3. func main() {
  4. dwarfs := make([]string, 0, 10)
  5. dwarfs = append(dwarfs, "Ceres", "Pluto", "Haumea", "Makemake", "Eris")
  6. fmt.Println(dwarfs)
  7. }

声明可变参数的函数

  • 声明 Printf、append 这样的可变参数函数,需要在函数的最后一个参数前面加上 … 符号。
  1. package main
  2. import "fmt"
  3. func terraform(prefix string, worlds ...string) []string {
  4. newWorlds := make([]string, len(worlds))
  5. for i := range worlds {
  6. newWorlds[i] = prefix + " " + worlds[i]
  7. }
  8. return newWorlds
  9. }
  10. func main() {
  11. twoWorlds := terraform("New", "Venus", "Mars")
  12. fmt.Println(twoWorlds)
  13. planets := []string{"Venus", "Mars", "Jupiter"}
  14. newPlanets := terraform("New", planets...)
  15. fmt.Println(newPlanets)
  16. }

作业

  • 编写一个程序:
    • 通过循环,持续的将元素追加到 slice 里
    • 在 slice 容量发生变化的时候打印出它的容量
    • 请判断 append 函数在底层数组的空间被填满之后,是否会将数组的容量增加一倍?
  1. package main
  2. import "fmt"
  3. func main() {
  4. s := []string{}
  5. lastCap := cap(s)
  6. for i := 0; i < 10000; i++ {
  7. s = append(s, "An element")
  8. if cap(s) != lastCap {
  9. fmt.Println(cap(s))
  10. lastCap = cap(s)
  11. }
  12. }
  13. }