slice 的长度,容量以及扩容

87 阅读3分钟

1.长度和容量的含义

切片的长度len()是它所包含的元素实际个数。
切片的容量cap()是从它的第一个元素到其【底层数组/切片】最后一个元素的个数。

func f1() {
   array := [10]int32{1, 2, 3, 4, 5} //[1,2,3,4,5,0,0,0,0,0]
   temp := make([]string, 0)
   ss := []string{"武汉", "西安", "苏州"}
   temp = append(temp, ss...) //...的意思是将上面的string数组拆开挨个放到ss位置上
   fmt.Println(temp, len(temp), cap(temp))

   s1 := array[:5] //【左闭右开原则】所以是:0-4角标=[1,2,3,4,5]
                   //s1是从array数组身上切来的,那么【底层数组】就是array
                   //s1的第一个元素是1,array的最后一个元素是角标9号的0,对应在array数组中,容量计算出就是10
   fmt.Println(s1, len(s1), cap(s1)) //[1,2,3,4,5],5,10
   s2 := s1[5:10]                    //明显 s1 拿不到数据,所以都赋值为0,[0,0,0,0,0]
   fmt.Println(s2)
}

2.扩容是深拷贝,那容量机制是???

1.若新申请容量(newcap)新增了 1倍还多的旧容量(oldcap),最终容量(newcap)就是新申请的容量(cap)

2.1否则判断,如果 oldlen<1024,则最终容量(newcap)就是旧容量(oldcap)的两倍

2.2否则判断,如果 oldlen>1024,则最终容量(newcap)从旧容量(oldcap)开始循环增加原来的1/4 直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)

3.如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(newcap)

注意:切片扩容会根据元素的类型而做不同的处理,比如int和string类型的处理方式就不一样

func f2() {
   array := [10]int32{1, 2, 3, 4, 5}
   s1 := array[:5:5]  //【左闭右开原则】所以是:0-4角标=[1,2,3,4,5],容量参数为5,实际容量为5-0=5
   s2 := array[:5:10] //明显 s1 拿不到数据,所以都赋值为0,[0,0,0,0,0],容量参数为10,实际容量为10-5=5

   fmt.Println(s1, len(s1), cap(s1))//[1 2 3 4 5] 5 5
   fmt.Println(s2, len(s2), cap(s2))//[1 2 3 4 5] 5 10
   fmt.Printf("%p\n", s1)

   s1 = append(s1, 6)                //因为s1的是5,原本是没法追加的,所以要深拷贝一份再追加
   fmt.Println(s1, len(s1), cap(s1)) //[1,2,3,4,5,6],6,12【这里的12不应该是10???】
   fmt.Printf("%p\n", s1)            //会发现s1的地址发生了变化,说明s1扩容是重新找的地址,开一块新的空间
   a1 := []int{1, 2, 3}
   fmt.Println(a1, len(a1), cap(a1)) //[1 2 3] 3 3
   a1 = append(a1, 5)
   fmt.Println(a1, len(a1), cap(a1)) //[1 2 3 5] 4 6
}

s1 的长度,容量不应该是 6 10 ???
看下面对比:

func main() {
   array := [10]int32{1, 2, 3, 4, 5}
   s1 := array[:5:5]                 //【左闭右开原则】所以是:0-4角标=[1,2,3,4,5],容量参数为5,实际容量为5-0=5
   fmt.Println(s1, len(s1), cap(s1)) //[1 2 3 4 5] 5 5
   s1 = append(s1, 6)                //因为s1的是5,原本是没法追加的,所以要深拷贝一份再追加
   fmt.Println(s1, len(s1), cap(s1)) //[1,2,3,4,5,6],6,12

   s2 := array[:5]                   //【左闭右开原则】所以是:0-4角标=[1,2,3,4,5]
   fmt.Println(s2, len(s2), cap(s2)) //[1 2 3 4 5] 5 5
   s2 = append(s2, 6)                //因为s1的是5,原本是没法追加的,所以要深拷贝一份再追加
   fmt.Println(s2, len(s2), cap(s2)) //[1,2,3,4,5,6],6,10
}

发现不同就是因为s1在截取array的时候有没有设定容量参数
所以:不设置容量参数才是符合扩容机制的