go中的slice

组成

指针、长度和容量

大小

slice占用24个字节
array:执向底层数组的指针,占用8个字节
len:切片的长度,占用8个字节
cap:切片的容量,占用8个字节

四种初始化方式

var slice1 []int
slice2 := []int{1, 2, 3, 4}
slice3 := make([]int,3,5)
// 从其他array中街区
slice4 := arr[1:3]

slice和array的区别

数组的长度固定,数组是值类型(值类型有基本数据类型,结构体类型),slice的值长度可变,属于引用类型(引用类型:字典类型,通道类型,函数类型)切片是引用传递,所以它们不需要使用额外的内存并且比使用数组更有效率

扩容策略

  1. 首先判断,如果新申请的容量(cap)大于2倍的旧容量(old.cap),最终容量就是新申请的容量
  2. 否则判断,如果旧切片的长度(len)小于1024,则最终容量就是旧容量的两倍
  3. 否则判断,如果旧切片长度(len)大于等于1024,则最终容量从旧容量开始循环增加原来的1/4,直到最终容量大于等于新申请的容量
  4. 如果最终容量计算值溢出,则最终容量就是新申请容量

slice深拷贝和浅拷贝

浅拷贝

a := []int{1, 2, 3}
b := a //地址的拷贝,浅拷贝
fmt.Println(a, b)

a[0] = 1000
fmt.Println(a, b)

b[2] = 4000
fmt.Println(a, b)

/*
执行结果
[1 2 3] [1 2 3]
[1000 2 3] [1000 2 3]
[1000 2 4000] [1000 2 4000]
*/

深拷贝

a := []int{1, 2, 3}
b := make([]int, 0)     //创建新切片
b = append(b, a[:]...)
fmt.Println(a, b)
a[1] = 1000
fmt.Println(a, b)
fmt.Printf("%p,%p", a, b)

/*
执行结果
[1 2 3] [1 2 3]
[1 1000 3] [1 2 3]
0xc0000b4018,0xc0000b4030% 
*/

线程安全性

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

func main() {
 var s []string
 for i := 0; i < 9999; i++ {
  go func() {
   s = append(s, "脑子进煎鱼了")
  }()
 }

 fmt.Printf("进了 %d 只煎鱼", len(s))
}

执行结果肯定对不上9999,这就是索引位覆写
解决办法:

  1. 加互斥锁
package main

import (
	"fmt"
	"sync"
	"time"
)

var s []int
var lock sync.Mutex 

func appendSlice(i int) {
	lock.Lock()
	s = append(s, i)
	lock.Unlock()
}

func main() {
	
	for i := 0; i < 10000; i++ {
		go appendSlice(i)
	}
	time.Sleep(time.Second)
	for i, v := range s {
		fmt.Println(i, ":", v)
	}
}
  1. 使用协程来维护slice
for {
    data := <- chanList
    list = append(list, data)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值