Golang append有趣的例子

例子1

参考:https://stackoverflow.com/questions/20195296/golang-append-an-item-to-a-slice

package main

import "fmt"

func main(){
  a:=make([]int,1,2)
  a[0]=0
  fmt.Println("before: ", a,len(a),cap(a))
  T(a)
  fmt.Println("after: ",a,len(a),cap(a)) // place 1
}

func T(a []int){
	a=append(a,100)
	fmt.Println("in T: ",a,len(a),cap(a))
}
// 输出:
before:  [0] 1 2
in T:  [0 100] 2 2
after:  [0] 1 2

为什么执行了T函数打印的a的内容没变

  • 输出:after: [0] 1 2
  • 其实如果你改成
    fmt.Println("after: ",a[:2],len(a),cap(a))
    
    看到的输出如下:
    after: [0 100] 1 2

注意

  • 输出由[0] 变成了[0 100],内容变了, 但是len(a)依然是1

为什么

  • fmt.Println("after: ",a,len(a),cap(a))这里打印的时候,只打印了len(a)个元素出来,也就是只打印了a[0]
  • fmt.Println("after: ",a[:2],len(a),cap(a))这里打印的时候,就全部打印了出来
  • 因为前后的len(a)都为1,所以你不能执行a[1],会panic,因为越界

例子2

参考:https://stackoverflow.com/questions/28115599/when-does-golang-append-create-a-new-slice

package main

import "fmt"

func main() {
    s := make([]int, 0, 5)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
    fmt.Println(a)

    b := append(s, 6)
    fmt.Println(a)
    fmt.Println(b)
}
// 输出
[1 2 3 4 5]
[1 2 3 4 6]
[1 2 3 4 6]

疑惑

  • 为什么b改变了,a也改变了?

解惑

  • 因为a和b都是在s上append的元素,s的cap是5,append一个元素不会导致扩容,s的底层数组的地址没变,s和a和b都指向了同一个底层数组,当b变了,也就改变了底层数组,所以a也变了
  • 这里有一个不太好理解的解答,说是覆盖了。。。

如何证明s,a,b的底层数组地址都没变

package main

import "fmt"

func main() {
    s := make([]int, 0, 5)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
	fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
    fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
	fmt.Println("========")
    b := append(s, 6)
    fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
    fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
    fmt.Printf("%v,%v,%v\n",b, cap(b), &b[0])
}
// 输出:
[1 2 3 4],5,0xc000010420
[1 2 3 4 5],5,0xc000010420
========
[1 2 3 4],5,0xc000010420
[1 2 3 4 6],5,0xc000010420
[1 2 3 4 6],5,0xc000010420

又问, 为什么s的打印值没变,不是s,a,b指向同一个底层数组么?

package main

import "fmt"

func main() {
    s := make([]int, 0, 5)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
    fmt.Println(a)
	fmt.Println(s)
	
	fmt.Println("===")
    b := append(s, 6)
    fmt.Println(a)
    fmt.Println(s)
	fmt.Println(b)
}
// 输出
[1 2 3 4 5]
[1 2 3 4]
===
[1 2 3 4 6]
[1 2 3 4]
[1 2 3 4 6]

再解惑:

其实就是上面的例子1,如果你改成fmt.Println(s[:5])就会发现输出完全是一样的

再问:如果s的cap改成4呢?

package main

import "fmt"

func main() {
    s := make([]int, 0, 4)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
    fmt.Println(a)
	
	fmt.Println("===")
    b := append(s, 6)
    fmt.Println(a)
	fmt.Println(b)
}
// 输出
[1 2 3 4 5]
[1 2 3 4 5]
[1 2 3 4 6]

再解惑

因为每次a和b在s上append,都导致了超过了s的cap,所以底层都指向了新的数组,a,b,s的底层数组都不一样了

package main

import "fmt"

func main() {
    s := make([]int, 0, 4)
    s = append(s, []int{1, 2, 3, 4}...)

    a := append(s, 5)
	fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
    fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
	fmt.Println("========")
    b := append(s, 6)
    fmt.Printf("%v,%v,%v\n",s, cap(s), &s[0])
    fmt.Printf("%v,%v,%v\n",a, cap(a), &a[0])
    fmt.Printf("%v,%v,%v\n",b, cap(b), &b[0])
}
// 输出
[1 2 3 4],4,0xc00000c3c0
[1 2 3 4 5],8,0xc00000e540
========
[1 2 3 4],4,0xc00000c3c0
[1 2 3 4 5],8,0xc00000e540
[1 2 3 4 6],8,0xc00000e580

一个关于扩容的不算误区的误区

slice的扩容总是两倍,其实实际的算法比这个复杂,并不一定是2倍

  • 可以看看这个链接里的growslice函数,其中的一段:
    newcap := old.cap
    	doublecap := newcap + newcap
    	if cap > doublecap {
    		newcap = cap
    	} else {
    		if old.cap < 1024 {
    			newcap = doublecap
    		} else {
    			// Check 0 < newcap to detect overflow
    			// and prevent an infinite loop.
    			for 0 < newcap && newcap < cap {
    				newcap += newcap / 4
    			}
    			// Set newcap to the requested cap when
    			// the newcap calculation overflowed.
    			if newcap <= 0 {
    				newcap = cap
    			}
    		}
    	}
    
  • 如果新申请的容量(cap)大于2倍的旧容量(old.cap),则最终容量是新申请的容量,否则
    • 如果旧切片的长度小于1024,则最终容量是旧容量的2倍,否则
      • 如果旧切片的长度大于等于1024,则最终就是新申请的容量,只是里面用一个for循环每次递增1/4

例子3

package main

import "fmt"

func main() {
	a := []int{1, 2, 3, 4}
	b := a[1:]
	b = append(b, 5)
	fmt.Println(a, b)
}

// [1 2 3 4] [2 3 4 5]

问:append了b的数据,a里的数据会变么?
答:不会变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值