Go36-7-数组和切片

数组和切片

数组(array)类型和切片(slice)类型:
相同:都属于集合类的类型,它们的值都可以用来存储某一种类型的值(或者说元素)。
不同:数组的长度是固定的,而切片是可变长的。

长度
数组的长度在声明的时候必须确定,并且之后不会再变。长度是其类型的一部分。
比如:[1]string 和 [2]string 是两个不同的类型。
切片的长度是可以随着其中元素的增长而增长的,但是不会随着元素的减少而减少。

底层数组
可以把切片看做是对数组的一层简单的封装,每个切片的底层数据结构中,一定会包含一个数组。这个数组可以被叫做切片的底层数组。而切片可以被看做是对数组的某个连续片段的引用。

值类型、引用类型

切片属于引用类型,数组属于值类型
引用类型:

  • 切片
  • 字典
  • 管道
  • 函数

值类型:

  • 数组
  • 结构体

长度和容量

数组和切片都用长度和容量。调用len函数,可以得到长度,调用cap函数可以得到容量。
数组的容量永远等于长度,并且是不可变的。
关于切片的容量和长度,看下面的例子:

package main

import "fmt"

func main() {
    s1 := make([]int, 5)
    fmt.Println(len(s1), cap(s1), s1)
    s2 := make([]int, 5, 8)
    fmt.Println(len(s2), cap(s2), s2)
}

先用make声明了一个[]int类型的变量s1,并且传递了一个第二个参数5。指明了切片的长度。
用同样了方式声明了切片s2,这次多传递了一个参数8,指明了切片的容量。
s1的长度是5,通过声明指定。容量也是5,声明时没有指定容量,就和长度一致。
s2的长度是5,通过声明指定。容量是8,也是通过声明进行指定。
下面的切片表达式,可以把s2扩展到其当前最大容量:

s2[0:cap(s2)]

扩容

当切片无法容纳更多的元素时,Go语言就会对切片进行扩容。扩容不会改变原来的切片,而是会生成一个容量更大的切片,然后把原有的元素和新元素拷贝到新切片中。
一般情况下,扩容会把新切片的容量(新容量)变成原来切片容量(原容量)的2倍。
当切片的长度大于或等于1024是,扩容是以1.25倍来增加的。
验证一下上面的扩容策略:

package main

import "fmt"

func main() {
    s1 := make([]int, 3)
    fmt.Println(len(s1), cap(s1), s1)  // 当前容量3
    s1 = append(s1, 1)
    fmt.Println(len(s1), cap(s1), s1)  // 容量翻倍,变成6
    s2 := make([]int, 1023)
    fmt.Println(len(s2), cap(s2))  // 当前容量1023
    s2 = append(s2, 1)
    fmt.Println(len(s2), cap(s2))  // 看着像翻倍,不过是1024的翻倍2048
    s3 := make([]int, 1024)
    s3 = append(s3, 1)
    fmt.Println(len(s3), cap(s3))  // 容量变为1.25倍
}
/* 执行结果
PS G:\Steed\Documents\Go\src\Go36\article07\example02> go run main.go
3 3 [0 0 0]
4 6 [0 0 0 1]
1023 1023
1024 2048
1025 1280
PS G:\Steed\Documents\Go\src\Go36\article07\example02>
*/

验证下来,似乎有一点小偏差。
另外,如果一次追加的元素过多,按上面的规则做一次扩容不够,最终还是会扩容到一个比需要的容量大一些或者正好的容量,不过具体情况有点复杂。可以自己试试看,下面的例子一次可以往切片里追加大量的元素:

package main

import "fmt"

func main() {
    s1 := make([]int, 5, 8)
    var s []int
    s = append(s1, make([]int, 88-5)...)
    fmt.Println(len(s), cap(s))
    s2 := make([]int, 1024)
    s = append(s2, make([]int, 2048-1024)...)
    fmt.Println(len(s), cap(s))
}

不必太在意切片“扩容”策略中的一些细节,只要能够理解它的基本规律,并可以进行近似的估算就可以了。
不过如果有兴趣,更多细节可参见runtime包中slice.go文件里的growslice及相关函数的具体实现。

替换底层数组

无需扩容时,append函数返回的是指向原底层数组的新切片。其实就是底层数组还在那,切片的下标往后加了几位,可以指向到底层数组后面更多的元素了。
需要扩容时,append函数返回的是指向新底层数组的新切片。会创建一个更大容量的新的底层数组,将元素从原切片复制到新的底层数组,然后追加新元素。
希望下面的例子可以说明这里的问题:

package main

import "fmt"

func main() {
    a := [...]int{1,2,3,4,5}  // 这是一个数组,将作为下面切片的底层数组
    s1 := a[:3]
    s1 = append(s1, 11)  // 未发生扩容
    fmt.Println(s1, a)  // s1的新底层数组还是a,往s1末尾添加元素,将覆盖a里原来的值
    s2 := a[:3]
    s2 = append(s2, 21, 22, 23, 24, 25)  // 容量不够,需要扩容
    fmt.Print(s2, a)  // 现在a不再是s2的底层数组了,这里会复制一份数组到一个新的数组,作为新的底层数组。a里的元素不会被覆盖
}

这里生成数组a的时候推导了数组的长度,长度也是数组类型的一部分,用上面的方法也可以把长度推导出来。

原文地址:http://blog.51cto.com/steed/2338995

时间: 2024-11-10 09:04:52

Go36-7-数组和切片的相关文章

GoLang笔记-数组和切片,本质是就是长度不可变的可变的区别

数组 Arrays 数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值.在初始化后长度是固定的,无法修改其长度.当作为方法的入参传入时将复制一份数组而不是引用同一指针.数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度. 初始化 数组的初始化有多种形式,查看示例代码 , 在线运行示例代码 [5] int {1,2,3,4,5} 长度为5的数组,其元素值依次为:1,2,3,4,5 [5] int {1,2} 长度为5的数组

GO语言总结(3)——数组和切片

上篇博文简单介绍了一下Go语言的基本类型——GO语言总结(2)——基本类型,本篇博文开始介绍Go语言的数组和切片. 一.数组 与其他大多数语言类似,Go语言的数组也是一个元素类型相同的定长的序列. (1)数组的创建. 数组有3种创建方式:[length]Type .[N]Type{value1, value2, ... , valueN}.[...]Type{value1, value2, ... , valueN} 如下: func test5() { var iarray1 [5]int32

go的数组和切片

对于GO的数组和切片的初步了解, 切边是数组的一个指针,切片的初始化有三种:一种是通过make,即 slice := make([]int,5,10) 另一种是通过:=指向一个已经存在的数组.即 slice := []int {1,2,3,4,5,6} 切片的切片不可初始化超过原始切片的cap值,比如: sliceA := make([]int,5,10) sliceB := sliceA[:11] 这个是不行的,11超过了sliceA的cap值(10),会报运行时异常. 对切片的append,

Go - 数组 和 切片

一.数组 与其他大多数语言类似,Go语言的数组也是一个元素类型相同的定长的序列. (1)数组的创建 数组有 3 种创建方式: 1) [length]Type 2) [length]Type{value1, value2, ... , valueN} 3) [...]Type{value1, value2, ... , valueN} 如下: func test5() { var arr1 [5]int32 var arr2 [5]int32 = [5]int32{1, 2, 3, 4, 5} a

Go数组、切片、映射的原理--简明解析

数组.切片.映射是Golang的最重要的数据结构,下面是对这3种数据结构的一点个人总结: 一.数组 数组是切片和映射的基础数据结构. 数组是一个长度固定的数据类型,存储着一段具有相同数据类型元素的连续内存块. 因为数组占用的内存是连续分配的,所以对数组的操作速度很快. 声明数组的方式:4种 var array1 [5]int array1 := [5]int{3,5,6,3,2} array1 := [...]int{3,4,7,8,1} //根据数组字面量中元素的个数来确定数组的长度 arra

GO的数组及切片

感觉在向PYTHON学一些数组方面的功能. package main import "fmt" func main() { var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} var mySlice []int = myArray[:5] fmt.Println("Elements of myArray: ") for _, v := range myArray { fmt.Print(v, &q

python运算学习之Numpy ------ 数组的切片索引与循环遍历、条件和布尔数组、

数组的切片索引: 数组的切片索引和列表非常类似,下面用代码简单说明 1 a = np.random.rand(16).reshape(4, 4) 2 print("数组a:\n", a) 3 print(a[-1][1:4]) 4 Out[1]: 5 数组a: 6 [[0.04175379 0.43013992 0.5398909 0.40638248] 7 [0.3305902 0.11958799 0.48680358 0.30755734] 8 [0.00893887 0.384

go 语言学习 - 数组和切片

package main import "fmt" func main(){ //数组 var a = [3]int{}//相当于[3]int{0,0,0} a[0] = 1 changeArray(a) fmt.Println(a) b := [...]int{1,2,3}//省略号符号表示让编译器根据后面初始化情况自动计算数组的长度,但这个长度是编译时确定的 fmt.Println(b) c := new([3]int) //new 返回的是地址,但是一样可以用 pointName

Go语言入门——数组、切片和映射

按照以往开一些专题的风格,第一篇一般都是“从HelloWorld开始” 但是对于Go,思来想去,感觉真的从“HelloWorld”说起,压根撑不住一篇的篇幅,因为Go的HelloWorld太简单了. 1.简介 Go是什么? Go(又称Golang)是Google开发的一种静态强类型.编译型.并发型,并具有垃圾回收功能的编程语言.——摘自百度百科 Github地址 https://github.com/golang/go 官网地址 https://golang.org 中文网社区 https://

数组和切片

实际上就是个数组.底层是一个数组,切片内有这个数组引用,和切片的信息.var arrAge = [5]int{18, 20, 15, 22, 16}var arrLazy = [...]int{5, 6, 7, 8, 22}//...可以忽略var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}切片; x.y是切片改变x.y就是改变数组,向x.y添加数据也是改变数组. make创建一个切片:make([]int,len,c