博客主页:看看是李XX还是李歘歘
每天分享一些包括但不限于计算机基础、算法等相关的知识点
点关注不迷路,总有一些知识点是你想要的
相同之处⛽️今天的内容是 Go的Slice和数组 ⛽️
- 属于集合类的类型,值都可以用来存储某一种类型的元素;
- 在内存布局中都是连续分配的。
- 数组的长度固定;slice的值长度可变。数组长度在声明的时候就必须给定,并且之后不会再改变。slice类型的字面量中只有元素类型,没有长度(可以在使用make函数时候初始化时添加长度和容量),切片长度可以自动随着元素数量的增长而增长,但不会随着元素数量的减少而减少。
-
数组是值类型(值类型有基本数据类型,结构体类型);切片属于引用类型(引用类型:字典类型,通道类型,函数类型),引用类型不需要使用额外的内存并且比使用数组更有效率。
-
如果传递的参数是引用类型,那么就是“传引用”,如果传递的是值类型,那么就是“传值”(会把以前的数据复制一遍)
-
数组的长度=容量,不可改变;切片长度<=容量,动态扩容;
slice的容量和长度都是可以进行动态扩容的。
type slice struct {
array unsafe.Pointer
len int
cap int
}
array:指向slice中第一个元素地址
len:slice长度
cap:slice容量
长度必须小于等于容量,通过len()和cap()函数分别返回slice的长度和容量。
关于长度和容量的定义,在源码中关于len()和cap()的定义如下:
| // The len built-in function returns the length of v, according to its type: // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). // Slice, or map: the number of elements in v; if v is nil, len(v) is zero. // String: the number of bytes in v. // Channel: the number of elements queued (unread) in the channel buffer; // if v is nil, len(v) is zero. // For some arguments, such as a string literal or a simple array expression, the // result can be a constant. See the Go language specification's "Length and // capacity" section for details. func len(v Type) int |
slice或者map:参数v中元素的个数,如果v是nil,则长度为0
| // The cap built-in function returns the capacity of v, according to its type: // Array: the number of elements in v (same as len(v)). // Pointer to array: the number of elements in *v (same as len(v)). // Slice: the maximum length the slice can reach when resliced; // if v is nil, cap(v) is zero. // Channel: the channel buffer capacity, in units of elements; // if v is nil, cap(v) is zero. // For some arguments, such as a simple array expression, the result can be a // constant. See the Go language specification's "Length and capacity" section for // details. func cap(v Type) int |
slice:当重新切片时,切片能够搜索到的最大长度。
在切片(slice)中,长度代表它包含的元素的个数(遍历时候,以len()为基准),容量代表从它的第一个元素开始计数,到其底层数组元素末尾的个数,所以说,len是永远小于等于cap的。
| func main() { a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println(len(a), cap(a)) //10 10 b := a[3:4] fmt.Println(len(b), cap(b)) // 1 7 } |
len(b)长度即4-3=1;cap(b)容量即10-3=7。
Slice的扩容- 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),扩容后切片的容量(newcap)就是新申请的容量(cap);
- 否则判断,如果旧切片的长度小于1024,则扩容后切片的容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap);
- 否则判断,如果旧切片长度大于等于1024,则扩容后切片的容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap+=newcap/4),直到扩容后切片的容量(newcap)大于等于新申请的容量(cap),即(newcap>=cap);
- 如果扩容后切片的容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。
slice之append时原数组发生变化的问题 append函数
append用于往slice中添加元素或者合并两个slice,函数append()总是会增加slice的长度,而容量可能回改变,也可能不会改变,这取决于被操纵切片的可用容量。
- 如果nums的cap够用,则会直接在nums指向的数组后面追加元素,返回的slice和原来的slice是同一个对象。显然,这种情况下原来的slice的值会发生变化!
- 如果nums的cap不够用,则会创建一个新的底层数组空间用来存储数据,将被引用的现有的值复制到新数组里,再添加元素,返回指向新数组的slice。这时候原来的slice指向的数组并没有发生任何变化!
补充:为了更好的使用append()函数实现追加元素,为底层数组提供一定的保护,建议使用切片的第三个索引来限制新切片的容量。
切片是引用类型,可能多个切片使用同一个底层数组,对切片的内容进行修改可能影响多个切片,如果能限制容量和长度相等,则追加元素时一定会新建一个切片。



