08 Golang引用类型——切片


本文摘自网络,作者,侵删。

切片

切片(slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。

切片是一个引用类型,它的内部结构包括地址、长度和容量。

声明切片类型的基本语法如下:

var name []T

其中name表示变量名,T表示切片中的元素类型

var arr1 []int
fmt.Printf("%v-%T-长度:%v", arr1, arr1, len(arr1))//[]-[]int-长度:4

方式二:定义时初始化

var arr1 = []int{1, 3, 5, 7}
fmt.Printf("%v-%T-长度:%v", arr1, arr1, len(arr1))//[]-[]int-长度:4

方式三:赋值时带下标

var arr1 = []int{1:2, 2:4, 5:6}
fmt.Printf("%v-%T-长度:%v", arr1, arr1, len(arr1))
//[0 2 4 0 0 6]-[]int-长度:6
nil

golang中,当声明了一个变量,却还并没有赋值值,golang中会自动赋值一个默认值。

类型 默认值
bool false
numbers 0
string ""
pointers nil
slices(切片) nil
maps/channels/functions nil

golang中声明切片的默认值是nil

var arr1 []int
fmt.Println(arr1 === nil) //true
切片的循环遍历
  • for
var strSlice = []string{"php", "java", "nodejs", "golang"}
for i := 0; i < len(strSlice); i++{
    fmt.Println(strSlice[i])
} 
  • for range
var strSlice = []string{"php", "java", "nodejs", "golang"}
    for k, v := range strSlice {
        fmt.Println(k, v)
    }
} 
基于数组定义切片

获取数组中的所有值

a := [5]int{1, 3, 5, 7, 9}
b := a[:]
fmt.Printf("%v-%T", b, b) //[1 3 5 7]-[]int

截取数组的某一部分[)——左包右不包

a := [5]int{1, 3, 5, 7, 9}
b := a[1:4]
c := a[3:]//获取第三个下标之后的数据
d := a[:3]//获取第三个下标之前的数据
fmt.Printf("%v-%T\n", b, b)//[3 5 7]-[]int
fmt.Printf("%v-%T", c, c)//[5, 7]-[]int
fmt.Printf("%v-%T", d, d)//[1 3 5]-[]int
切片再切片

除了基于数组得到切片,还可以通过切片来得到切片

a := []string{"北京","上海","广州","深圳","成都","重庆"}
b := a[1:]
fmt.Printf("%v-%T\n", b, b)//[上海 广州 深圳 成都 重庆]
切片的长度及容量

切片拥有自己的长度和容量,可以通过内置的len()函数求长度,cap()求容量。

切片的本质就是对底层数组的封装,它包含了三个信息:底层数据的指针、切片的长度(len)和切片的容量(cap)

长度是它所包含的元素个数

容量是从它的第一个元素开始,到其底层数组元素末尾的个数

s := []int{1, 2, 3, 4, 5, 6}
fmt.Printf("长度%d 容量%d", len(s), cap(s))
//长度6 容量6
a := s[2:]
fmt.Printf("长度%d 容量%d", len(a), cap(a))
//长度4 容量4
b := s[1:3]
fmt.Printf("长度%d 容量%d", len(b), cap(b))
//长度2 容量5
c := s[:3]
fmt.Printf("长度%d 容量%d", len(c), cap(c))
//长度3 容量6
make()函数来构造切片

make([]T, size, cap)声明一个长度为size,容量为cap的切片

var sliceA = make([]int, 4, 8)
fmt.Println(sliceA)//[0 0 0 0]
fmt.Printf("%d-%d",len(sliceA), cap(sliceA))//4-8
切片的修改
var sliceA = make([]int, 4, 8)
sliceA[0] = 10
sliceA[1] = 12
fmt.Println(sliceA)//[10 12 0 0]

sliceB := []string{"js", "java", "go"}
sliceB[2] = "node"
fmt.Println(sliceB)//[js java node]
切片的扩容append

golang中不能通过下标的方式给切片扩容,需要用到append()方法,类似于js中的concat()

append()后面可以传多个参数

var sliceC []int
fmt.Printf("值%v-长度%v-容量%v",sliceC,len(sliceC),cap(sliceC))//值[]-长度0-容量0
sliceC = append(sliceC, 12)
fmt.Printf("值%v-长度%v-容量%v",sliceC,len(sliceC),cap(sliceC))//值[12]-长度1-容量1

append合并切片

sliceA := []string{"php","java"}
sliceB := []string{"node","go"}
sliceA = append(sliceA, sliceB...)
fmt.Println(sliceA)//[php java node go]
  • 切片的扩容策略
  1. 首先判断,如果新申请容量大于2倍的旧容量,最终容量是新申请的容量
  2. 否则判断,如果旧切片的长度小于1024,则最终容量是旧容量的两倍;如果旧切片长度>=1024,则最终容量从旧容量开始循环增加原来的1/4,即(newcap = old.cap, for{newcap += newcap / 4}),直到最终容量大于新申请的容量
  3. 如果最终容量计算值溢出,则最终容量就是新申请容量

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

var sliceA []int
for i := 0; i < 5; i++{
    sliceA = append(sliceA, i)
    fmt.Printf("%v长度%d 容量%d", sliceA, len(sliceA), cap(sliceA))
}
//[1]长度1 容量1
//[1 2]长度2 容量2
//[1 2 3]长度3 容量4
//[1 2 3 4]长度4 容量4
//[1 2 3 4 5]长度5 容量8

可以通过查看$GOROOT/src/runtime/slice.go源码,其中扩容相关代码如下:

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
    newcap = cap
}else {
    if old.len < 1024{
        newcap = doublecap
    } else {
        for 0 < newcap && newcap < cap {
            newcap += newcap / 4
        }
        if newcap <= 0 {
            newcap = cap
        }
    }
}
使用copy()函数复制切片

切片是引用数据类型

sliceA := []int{1, 2, 3}
sliceB := sliceA
sliceB[0] = 11
fmt.Println(sliceA)//[11 2 3]
fmt.Println(sliceB)//[11 2 3]

使用copy复制切片(浅拷贝)

sliceA := []int{1, 2, 3}
sliceB := make([]int, 4, 4)
copy(sliceB, sliceA)//把A复制到B中
sliceA[0] = 11
fmt.Println(sliceA)//[11 2 3]
fmt.Println(sliceB)//[1 2 3]
从切片中删除元素

Go语言中并没有删除切片元素的专用方法,可以使用切片本身的特性来删除元素

a := []int{1,2,3,4}
//要删除索引为2的元素(3)
a = append(a[:2],a[3:]...) 
fmt.Println(a)//[1 2 4]

注意:append合并切片时,最后一个元素后面加...


本文来自:简书

感谢作者:learninginto

查看原文:08 Golang引用类型——切片

相关阅读 >>

详解 Golang 依赖管理之 mod

手撸Golang 行为型设计模式 命令模式

string,byte,rune

聊聊dapr的consistent hash

手撸Golang Go与微服务 saga模式之7

Go - 统一定义 api 错误码

Golang in pingcap

Go语言都用什么框架

Go语言基础

Go语言 break 语句

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...