golang笔记——数组与切片
一、切片的定义
我们可以从数组(go语言中很少直接使用数组)或者切片来初始化一个新的切片,也可以直接通过 make 来初始化一个所有元素为默认零值的切片。
//1、通过数组来初始化切片
arr := [...]int{, , , , }
slice1 := arr[:] // [1,2,3,4,5]
slice2 := arr[:] // [3,4]
slice3 := arr[:] // [1,2,3,4]
slice4 := arr[:] // [3,4,5] //2、通过切片来初始化切片
slice5 := slice1[:] //[4,5] //3、直接初始化。可以通过 make 来初始化,格式为 make([]int, len, cap),所有的元素初始化为默认零值;也可以直接初始化。
slice6 := make([]int, , ) // [0,0,0]
slice7 := []int{, , , , } // [1,2,3,4,5]
和其它大多数编程语言类似,Go语言里的这种索引形式也采用了左闭右开区间,包括m~n的第一个元素,但不包括最后那个元素(译注:比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最后一个元素)。这样可以简化我们的处理逻辑。比如s[m:n]这个slice,0 ≤ m ≤ n ≤ len(s),包含n-m个元素。
二、切片的特点
slice 有点类似 C++ 中的 vector,都是可变长数组,有容量的概念(当切片容量不够时,它会以2倍率来扩张),但是也有很多不同,如 slice 是基于数组的(该数组称为底级数组),slice 的长度是从底级数组中取出来的元素个数,容量是底级数组的最大索引与取出元素的最小索引的差值。array 是值类型,但 slice 却是引用类型。
func func1(s []int) {
s[] *= //会修改原切片的值
} func func2(s []int) {
for _, v := range s { //不会修改原切片的值,因 for ... range 是只读的
v *=
}
} func func3(s []int) {
for i := ; i < len(s); i++ { //会修改原切片的值
s[i] *=
}
} func main() {
s := []int{, , , , }
//func1(s) // 10,2,3,4,5,
//func2(s) //1,2,3,4,5,
//func3(s) //10,20,30,40,50,
for _, v := range s {
println(v, ",")
}
}
如果修改某个数组的数据,则基于此数组的切片也会发生改变,反之亦然(基于同一数组的多个切片之间也会互相影响)。这与切片的存储结构有关,切片的存储结构是,依次是底层数组的指针、切片长度、切片容量、切片数据。我们知道,当切片的容量发生变量时,会重新分配地址,所以此时切片和原底级数组的关联就会断开,修改不再影响对方,这一点要注意。
a := [...]int{, , , , }
s1 := a[:]
s2 := a[:]
s1[] =
print_array(a[:]) // [10,2,3,4,5] 修改切片会影响到底级数组,反之亦然
s2[] =
print_array(s1) // [20,2,3,4,5] 修改切片会影响到基于同一底级数组的切片 s1 = append(s1, []int{, }...) //append 时容量不够时会导致重新分配内存,与原底级数组关系断开
s1[] =
print_array(s1) // [100,2,3,4,5,6,7]
print_array(a[:]) //[20,2,3,4,5]
print_array(s2) //[20,2,3,4,5]
//print_array 为自行封装的格式化输出切片的方法
切片的删除并没有内置函数,如果需要删除切片 list 的第 k 个元素,我们一般可以通过下面三种方式操作:
1、通过 copy 方式:
func remove(slice []int, index int) []int {
copy(slice[index:], slice[index+:])
return slice[:len(slice)-]
}
2、通过append方式
func remove(slice []int, index int) []int {
return append(slice[:k], slice[k+1:]...)
}
3、如果该slice不关心删除后元素的排序顺序,可以简单将要删除的元素值设置为最后一个元素值,然后取 [:len(slice)-1] 的子切片就好了
func remove(slice []int, i int) []int {
slice[i] = slice[len(slice)-]
return slice[:len(slice)-]
}
三、切片的常用操作总结
切片的操作比数组复杂一些,因为切片是可变长的,而且有容量的概念。当切片容量不够时,它会以2倍率来扩张。
1、获取切片长度:len(slice)
2、获取切片容量:cap(slice)
3、为切片追加元素:slicename = append(slicename, element1, element2,...)
该函数是可变参函数,可变参部分也可传入另一个切片,形式为 slicename = append(slicename, slicename2...) ,可以参考可变参函数的用法。其实之前我一直疑惑为什么要再赋值给原切片,其实是因为,但append函数调用时,如果切片容量不够的话,会创建一个新的切片变量,这应该也就是为什么要赋值给原切片的原因吧。
4、切片没有提供删除元素的方法,但可以通过子切片的方式来达成此目的,但无法直接的从中间删除元素;也不能从中间插入元素。
5、切片的复制: copy(target_slice, source_slice)
复制切片要保证目标切片有足够长度(注意与容量无关),如果目标切片长度不足,则只复制到目标切片长度的数据,而不会自行改变目标切片长度来填充其它数据。
四、特别注意
把切片作为函数参数时,如果函数内部修改了此切片某些索引的值,会影响到原切片; 但如果在函数内部删除或添加了切片长度,则不会影响到原切片。是不是很诡异?
有人说是由扩容切片导致内存重新分配导致的,瞎扯淡,减小切片容量难道也会内存重新分配吗?只要是把切片作为参数,函数内部就已经使用了该切片的一个副本,切片的每个元素对应的地址,也同时被复制了,所以当修改某个元素时,是在那个元素的地址上进行修改的,所以能影响到原切片,而增加删除元素,是在切片副本上操作的,比如增加了一个元素,由于切片地址是连续的,经过测试发现原切片末尾地址后面的+n长度(由元素类型决定)的地址上也确实有值了,而且值就是函数内增加的那个元素值!说明函数内修改切片确实是修改了内存数据的!但是!原切片并不知道切片的长度发生了变化!!!函数内的修改只是让切片副本知道了长度的变化!所以原切片认为它的长度仍然是之前的长度! 而通过引用方式将切片指针作为参数,就不存在这个问题了。
如果要以引用方式传切片,请务必将切片地址作为参数传入。尽量避免将切片作为参数传入,如果真的只需要传值,可以在调用函数时,只传入原切片的一个临时副本变量(该方式不好,依赖于调用者),或者在函数开始处将对传入切片做一个临时副本,后面的处理只操作这个临时副本。期待更好的方法,最好有一个什么特殊操作符,可以指定切片能以完全传值方式工作就好了。
golang笔记——数组与切片的更多相关文章
- GoLang笔记-数组和切片,本质是就是长度不可变的可变的区别
数组 Arrays 数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值.在初始化后长度是固定的,无法修改其长度.当作为方法的入参传入时将复制一份数 ...
- golang之数组与切片
数组 数组可以存放多个同一类型数据,数组也是一种数据类型,在Go中,数组是值类型. 数组的定义: var 数组名 [数组大小]数据类型 var a [5]int 赋初值 a[0] = 1 a ...
- 『GoLang』数组与切片
数组 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列(这是一种同构的数据结构):这种类型可以是任意的原始类型例如整型.字符串或者自定义类型. 数组长度必须是一个常量表达式,并且必须是一个非负 ...
- golang学习笔记 ---数组与切片
数组: golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量表达式 ...
- golang 学习笔记 ---数组/字符串/切片
数组 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成.数组的长度是数组类型的组成部分.因为数组的长度是数组类型的一个部分,不同长度或不同类型的数据组成的数组都是不同的类 ...
- 深入学习golang(1)—数组与切片
数据(array)与切片(slice) 数组声明: ArrayType = "[" ArrayLength "]" ElementType . 例如: va ...
- golang中数组与切片的区别
初始化:数组需要指定大小,不指定也会根据初始化的自动推算出大小,不可改变 数组: a := [...],,} a := [],,} 切片: a:= [],,} a := make([]) a := m ...
- [Golang学习笔记] 07 数组和切片
01-06回顾: Go语言开发环境配置, 常用源码文件写法, 程序实体(尤其是变量)及其相关各种概念和编程技巧: 类型推断,变量重声明,可重名变量,类型推断,类型转换,别名类型和潜在类型 数组: 数组 ...
- go 学习笔记之数组还是切片都没什么不一样
上篇文章中详细介绍了 Go 的基础语言,指出了 Go 和其他主流的编程语言的差异性,比较侧重于语法细节,相信只要稍加记忆就能轻松从已有的编程语言切换到 Go 语言的编程习惯中,尽管这种切换可能并不是特 ...
随机推荐
- Windows 10升级如何立即进行,不用等微软分批推送?
如果你已经在 Windows 7/8.1 中预订,但现在还没收到 Windows 10 推送,可以试试下面的方法加速流程! 绝招:下载Windows 10 Media Creation Tool立即启 ...
- poj1006 / hdu1370 Biorhythms (中国剩余定理)
Biorhythms 题意:读入p,e,i,d 4个整数,已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i ,求n . (题在文末) 知识点:中国剩余定理 ...
- 洛谷P1262 间谍网络[强连通分量 BFS]
题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报 ...
- SQL语句的使用
SELECT b.level, a.cnt FROM (SELECT `level`,COUNT(*) AS cnt FROM wlsjlog GROUP BY level) a LEFT JOIN ...
- jmeter(六)元件的作用域与执行顺序
jmeter是一个开源的性能测试工具,它可以通过鼠标拖拽来随意改变元件之间的顺序以及元件的父子关系,那么随着它们的顺序和所在的域不同,它们在执行的时候,也会有很多不同. jmeter的test pla ...
- java构造方法的作用以及简单java类
public class TestDemo{ public static void main(String args[]){ Emp emp1 =new Emp(001,"tom" ...
- jquery替换所有逗号
代码: var aa= $("input[name=aa]").val().replace(/\,/g,""); 原文:http://blog.csdn.net ...
- YOLO: Real-Time Object Detection
YOLO detection darknet框架使用 YOLO 训练自己的数据步骤,宁广涵详细步骤说明
- socketAPI:一个最简单的服务器和对应的客户端C语言的实现
基于linux,该实例实现了服务端传了一个hello world给客户端.socket()创建socketbind()绑定socket到IP地址和端口listen()服务器监听客户端的连接connec ...
- JS组件系列——Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
前言:最近项目里面需要用到表格的冻结列功能,所谓“冻结列”,就是某些情况下表格的列比较多,需要固定前面的几列,后面的列滚动.遗憾的是,bootstrap table里自带的fixed column功能 ...