初片是基于数组实现的,它的底层是数组,可以理解为对底层数组的抽象。

源码包中src/runtime/slice.go定义了slice的数据结构:

type slice struct {
array unsafe.Pointer
len int
cap int
}

slice占用24个字节

array:指向底层数组的指针,占用8个字节
len:切片的长度,占用8个字节
cap:切片的容量,cap总是大于等于len 的,占用8个字节
len(slice)和cap(silce)大小是不一样的


slice有4种初始化方式

//初始化方式1:直接声明var slice1 []int

//初始化方式2:使用字面量
slice2 := []inti1,2,3,4}

//初始化方式3:使用make创建slice
slice3 := make( [ ]int,3,5)

//初始化方式4:从切片或数组“截取"
slcie4 := arr[1:3]

slice的深拷贝和浅拷贝

深拷贝∶拷贝的是数据本身,创造一个新对象,新创建的对象与原对象不共享内存,新创建的对象在内存中开辟一个新的内存地址,新对象值修改时不会影响原对象值

实现深拷贝的方式:

1.copy(slice2, slice1)
2.遍历append赋值

func main() {
    slice1 := []int{1,2,3,4,5}
    slice2 := make( []int,5,5)
    fmt.Printf(""slice1: %v,%p", slice1, slice1)
    copy(slice2, slice1)
    fmt.Printf(""slice2: %v,%p", slice2, slice2)
    slice3 := make( []int, 0,5)
    for _, v := range slice1{
    slice3 = append(slice3,v)
    }
    fmt.Printf(""slice3:%v,%sp",slice3, slice3)
}
slice1: [1 2 34 5], oxce000b0030
slice2: [1 2 34 5], oxc0000b0060
slice3: [1 2 3 4 5],oxc0000b0090

浅拷贝︰拷贝的是数据地址,只复制指向的对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化

实现浅拷贝的方式:

1.引用类型的变量,默认赋值操作就是浅拷贝

slice2=slice1

2.传参数的时候


slice扩容机制

扩容会发生在slice append的时候,当slice的cap不足以容纳新元素,就会进行扩容,扩容规则如下

1.扩容为2倍的时候会申请新的空间
2.如果原有slice长度小于1024,那么每次就扩容为原来的2倍
3.如果原 slice长度大于等于1024,那么每次扩容就扩为原来的1.25倍


slice为什么不是线程安全

先看下线程安全的定义:
多个线程访问同一个对象时,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

再看Go语言实现线程安全常用的几种方式:
1.互斥锁
2.读写锁
3.原子操作
4. sync.once
5.sync.atomic
6.channel

slice底层结构并没有使用加锁等方式,不支持并发读写,所以并不是线程安全的,使用多个gorcutine对类型为 slice的变量进行操作,每次输出的值大概率都不会一样,与预期值不一致; slice在并发执行中不会报错,但是数据会丢失