一、切片
切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型,它的内部结构包含地址
、长度
和容量
。切片一般用于快速地操作一块数据集合。
二、切片的实战
package main
import "fmt"
func main() {
//切片 切片是一种动态数组,比数组操作起来更加方便,长度不固定,可以动态的追加和删除
/**
len() cap() 返回的结果可以相同或者不同
len() :指的是长度
cap() :指的是切片的容量
*/
// nil 空的切片
var s1 [] int
fmt.Print(s1)
fmt.Printf("len=%d cap=%d slice=%v", len(s1), cap(s1), s1)
// 空切片
var s2 = [] int{}
fmt.Printf("len = %d cap =%d slice=%v", len(s2), len(s2), s2)
var s3 = [] int{1, 2, 3, 4, 5}
fmt.Printf("len =%d cap =%d slice=%v", len(s3), len(s3), s3)
// 使用make 函数构造切片
var s4 [] int = make([] int, 4, 5)
fmt.Printf("len =%d cap =%d slice=%v", len(s4), len(s4), s4)
s5 := make([] uint8, 2, 5)
fmt.Printf("len=%d cap=%d slice=%v", len(s5), cap(s5), s5)
// 截取切片
sli := [] int{1, 2, 3, 4, 5}
fmt.Print("\n==========我是分割线=============")
fmt.Printf("\n len=%d cap=%d slice=%v \n", len(sli), cap(sli), sli)
fmt.Print("sli[1] == ",sli[1] ,"\n")
fmt.Println("sli[1] == ",sli[1] ,"\n")
fmt.Println("sli[:] ==",sli[:],"\n")
// 截取前面1位
fmt.Println("sli[1:] == ",sli[1:])
// 获取数组中前两位
fmt.Println("sli[:2] == ",sli[:2])
// 获取数组区间值
fmt.Println("sli[0:3] == ",sli[0:3])
fmt.Println("sli[0:2:4] == ",sli[0:2:4])
fmt.Printf("len = %d cap=%d slice=%v \n",len(sli[0:2:4]),cap(sli[0:2:4]),sli[0:2:4])
//追加切片
sli = append(sli,6)
fmt.Println("sli == ",sli)
sli = append(sli,6,7,8)
fmt.Print("sli ==",sli,"\n")
//删除切片
// 删除尾部一个元素
fmt.Print("sli[:len(sli) -1] == ",sli[:len(sli) -1],"\n")
// 删除头部2个元素
fmt.Println("sli[2:] == ",sli[2:])
// 删除中间2个元素
arr2 := [] int{1, 2, 3, 4, 5,6,7,8}
arr2 = append(arr2[:1],arr2[1+2:]...)
fmt.Println("删除中间元素",arr2)
arr3 := [] int{1, 2, 3, 4, 5,6,7,8}
arr3 = append(arr3[:2],arr3[1+3:]...)
fmt.Println("删除中间元素",arr3)
}
三、切片的原理
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
我们可以根据下面这个例子来看下:
有一个数组 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
,切片s1 := a[:5]
四、切片的扩容
看下面的源码
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 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),最终容量(newcap)就是新申请的容量(cap)。
- 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap),
- 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
- 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。