Go 语言数组
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型。
数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
数组的长度不可改变。
声明数组
Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type
以上为一维数组的定义方式。例如以下定义了数组 balance 长度为 10 类型为 float32:
var balance [10] float32
与Java不一样,数组不是引用类型,所以声明数组后,数据的每个元素都有了对应数据类型的零值。
初始化数组
以下演示了数组初始化:
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
初始化数组中 {} 中的元素个数不能大于 [] 中的数字。
可以使用…来代替数组的长度,Go 语言会根据元素的个数来设置数组的大小:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
该实例与上面的实例是一样的,虽然没有设置数组的大小。
balance[4] = 50.0
以上实例读取了第五个元素。数组元素可以通过索引(位置)来读取(或者修改),索引从0开始,第一个元素索引为 0,第二个索引为 1,以此类推。
访问数组元素
数组元素可以通过索引(位置)来读取。格式为数组名后加中括号,中括号中为索引的值。例如:
var salary float32 = balance[9]
以上实例读取了数组balance第10个元素的值。
以下演示了数组完整操作(声明、赋值、访问)的实例:
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}
以上实例执行结果如下:
Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109
Go 语言切片(Slice)
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
定义切片
你可以声明一个未指定大小的数组来定义切片:
var identifier []type
与声明数据相比,切片不需要说明长度,但未初始化的切片是nil。
或使用make()函数来创建b并初始化切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
这里 len 是数组的长度并且也是切片的初始长度。
切片初始化
s :=[] int {1,2,3 }
len() 和 cap() 函数
切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
以下为具体实例:
package main
import "fmt"
func main() {
var numbers = make([]int,3,5)
printSlice(numbers)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
以上实例运行输出结果为:
len=3 cap=5 slice=[0 0 0]
切片截取
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers)
/* 打印原始切片 */
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int,0,5)
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
printSlice(number3)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
执行以上代码输出结果为:
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
numbers == [0 1 2 3 4 5 6 7 8]
numbers[1:4] == [1 2 3]
numbers[:3] == [0 1 2]
numbers[4:] == [4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=3 cap=7 slice=[2 3 4]
虽然切片值在上述方面受到了其容量的限制。但是我们可以通过另外一种手段对其进行不受限制的扩展。这需要用到内建函数append。append会对切片值进行扩展并返回一个新的切片值,使用方法如下:
package main
import "fmt"
func main() {
var s []string
fmt.Println(s==nil)
//创建并初始化容量为5的切片
mb:=make([]int,5)
for i:=0;i<5;i++{
fmt.Println(mb[i])
}
mb2:=append(mb,1)
fmt.Println(mb2[5])
}
切片的底层原理
切片在内存中的组织方式实际上是一个有 3 个域的结构体:指向相关数组的指针,切片长度以及切片容量。每个切片底层保留了一个数据的引用。
slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:3]
newSlice[0] = 10
fmt.Println(slice)
fmt.Println(newSlice)
fmt.Printf("%p\n", &slice[1])
fmt.Printf("%p\n", &newSlice[0])
运行结果:
10
10
这个例子证明了,新的切片和原切片共用的是一个底层数组,所以当修改的时候,底层数组的值就会被改变,所以原切片的值也改变了。当然对于基于数组的切片也一样的。