Go复合数据类型数组和切片
数组
声明&&初始化数组
- 数组声明需要指定元素及元素个数,语法格式如下
- 数组用来存储相同唯一类型的,一组已编号且长度固定的序列
var arr_name[num] arr_type
package main
import "fmt"
func main() {
// 写添加元素的个数
var test [5]string
fmt.Println(test)
for i:=0; i< 4; i++{
test[i+1] = "test"
}
fmt.Println(test)
// 不写添加元素的个数, 动态加载列表
var arry = [...]int{1, 2, 3, 4, 5, 6}
fmt.Println(arry)
}
/*
结果
[ test test test test]
[1 2 3 4 5 6]
*/
- 索引元素[下标]
package main
import "fmt"
func main() {
var test = [...]string{"1","2","3","4"}
fmt.Println(test[2])
}
// 结果 3
- 索引越界报错
invalid argument: index 10 out of bounds
- 索引越界报错,骗过编译器 panic 用一个变量表示索引
package main
import "fmt"
func main() {
var idx = 10
var test = [...]string{"1","2","3","4"}
fmt.Println(test[idx])
}
// 报错 骗过编译器越界
指针数组
- 数组的元素除了是某个类型外,还可以是某个类型的指针
- new函数返回一个TYPE类型的数据结构划分内存并执行默认的初始化操作,然后返回这个数据对象的指针
- 只声明不初始化,必须用new,
- 两个数组指针直接复制
package main
import "fmt"
func main() {
var arry1 [5] *int
fmt.Println(arry1)
arry1[0] = new(int)
arry1[1] = new(int)
arry1[2] = new(int)
arry1[3] = new(int)
arry1[4] = new(int)
*arry1[0] =10
*arry1[1] =10
*arry1[2] =10
*arry1[3] =10
*arry1[4] =10
fmt.Println(arry1)
for i:=0; i < len(arry1); i++{
fmt.Printf("%v\n",*arry1[i])
fmt.Printf("%v\n",arry1[i])
}
}
// 结果
/*
[<nil> <nil> <nil> <nil> <nil>]
[0xc00001e0b0 0xc00001e0b8 0xc00001e0c0 0xc00001e0c8 0xc00001e0d0]
10
10
10
10
10
*/
- 两个数组指针复制
- 内部存放的是指针,指向同一块地址,直接赋值,内容都一样,看起来是浅拷贝,
- 但其实是深拷贝
- 判定依据两个数据指针不同就是深拷贝,指针地址相同就是浅拷贝
package main
import "fmt"
func main() {
var arry1 [3]*string
arry2 := [3]*string{new(string), new(string), new(string)}
fmt.Println(arry1)
fmt.Println(arry2)
arry1 = arry2
fmt.Println(arry1)
for i:=0; i < len(arry2); i++ {
fmt.Printf("%d, %v, %v\n", i, arry1[i], *arry1[i])
fmt.Printf("%d, %v, %v\n", i, arry2[i], *arry2[i])
}
}
/*
结果
[<nil> <nil> <nil>]
[0xc000014260 0xc000014270 0xc000014280]
[0xc000014260 0xc000014270 0xc000014280]
0, 0xc000014260,
0, 0xc000014260,
1, 0xc000014270,
1, 0xc000014270,
2, 0xc000014280,
2, 0xc000014280,
*/
数组指针
- 数组是一个值类型,所有的值类型变量在赋值和作为参数传递时都将产生一次复制操作
- 从内存和性能上来看,在函数间船队数组是一个开销很大的操作,因为无论这数组有多厂,都会完整复制,并传递给函数
- 数组指针只需要很小传递
package main
import (
"fmt"
"unsafe"
)
func bigArryPoint(arr *[1e6]int64) {
fmt.Printf("[数组指针复制:大小:%d 字节]\n", unsafe.Sizeof(arr))
}
func bigArry(arr [1e6]int64){
fmt.Printf("[数组复制:大小:%d 字节]\n", unsafe.Sizeof(arr))
}
func main() {
var arry [1e6]int64
bigArry(arry)
bigArryPoint(&arry)
}
/*
[数组复制:大小:8000000 字节]
[数组指针复制:大小:8 字节]
*/
多维数组
- 多维数组的典型用例是平面坐标(而为数组)和三维坐标(三维数组)
- Golang的数组本身只有一个唯独,但是我们可以组合多个数组从而创建出多维数组
package main
import (
"fmt"
"unsafe"
)
func bigArryPoint(arr *[1e6]int64) {
fmt.Printf("[数组指针复制:大小:%d 字节]\n", unsafe.Sizeof(arr))
}
func bigArry(arr [1e6]int64){
fmt.Printf("[数组复制:大小:%d 字节]\n", unsafe.Sizeof(arr))
}
func main() {
arry := [4][2] int{{1, 2},{10, 20},{30, 40},{50, 60}}
fmt.Println(arry[3])
fmt.Println(arry[3][0])
}
/*
结果
[50 60]
50
*/
切片
- 切片是围绕动态数据来构建的
- 数组一旦创建就不能更改长度,但是切片可以按需求自动增长和缩小
- 增长是使用内置的append函数来实现的
- 缩小通过对切片的再次切片来实现
声明和初始化
var直接声明
package main
import (
"fmt"
)
func main() {
var s1 []int
fmt.Println(s1)
s1 = append(s1, 1)
s1 = append(s1, 1)
s1 = append(s1, 1)
fmt.Println(s1)
var s2 = []int{1,2,3}
fmt.Println(s2)
}
使用make
make([]类型, 长度, 容量)
- 以类型0值+容量的个数填充slice
package main
import "fmt"
func main() {
// 使用make初始化一个长度为0的slice
var s1 = make([]int, 0)
s1 = append(s1, 1)
s1 = append(s1, 2)
s1 = append(s1, 3)
fmt.Println(s1)
// 使用make,初始化一个长度为5,容量为5的slice
var s2 = make([]int, 5, 5)
s2 = append(s2, 1)
s2 = append(s2, 2)
s2 = append(s2, 3)
fmt.Println(s2)
}
/*
结果
[1 2 3]
[0 0 0 0 0 1 2 3]
*/
- new和make对比
- 简单说new只分配内存,make用于slice,map,和channel的初始化。
- 对比表格
函数名 | 适用范围 | 返回值 | 填充值 |
---|---|---|---|
new | new可以对所有类型进行内存分配 | new返回指针 | new填充零值 |
make | make只能创建类型(slice、map、channel) | make返回引用 | make填充非零值 |
通过切片创建新的切片
- 语法如下
slice[start:end:cap]
- 其中start表示从slice的第几个元素开始
- end控制切片的长度(end-i)
- cap控制切片的容量,如果没有给定cap,slice的长度值,则表示到底层数组的最尾部
- 新切片的长度 = end-start
- 新切片的容量 = cap-start