go--数组
数组的定义
数组是用来存储相同唯一类型的,一组已编号且长度固定的序列
数组的特点
- 固定长度:这意味着数组不可增长、不可缩减。想要扩展数组,只能创建新数组,将原数组的元素复制到新数组。
- 内存连续:这意味可以在缓存中保留的时间更长,搜索速度更快,是一种非常高效的数据结构,同时还意味着可以通过数值的方式 (arr[index]) 获取索引数组中的元素。
- 固定类型:固定类型意味着限制了每个数组元素可以存放什么样的数据,以及每个元素可以存放多少字节的数据。
数组声明需要指定元素类型及元素个数,语法格式如下:
var arr_name [num]arr_type
例如
package main
import "fmt"
func main() {
//三种定义数组的方式
var age1 [5]int = [5]int{22, 32, 43, 14, 15} //声明长度为5 类型为 int 的数组
var age2 = [5]int{11, 32, 43, 14, 15} //声明并初始化,写长度
age3 := [5]int{33, 32, 43, 14, 15}
age4 := [...]int{33, 32, 43, 14, 15} // ... 表示数组长度不固定
fmt.Println(age1)
fmt.Println(age2)
fmt.Println(age3)
fmt.Println(age4)
}
结果
[22 32 43 14 15]
[11 32 43 14 15]
[33 32 43 14 15]
[33 32 43 14 15]
我们在定义数组的时候,也可以给某个索引上的值赋值,没赋值的就使用默认值
package main
import "fmt"
func main() {
a := [5]int{3: 18} // 给索引为 3 的元素赋值为 18。没赋值的就使用默认值,int 的默认值为 0
fmt.Println(a)
}
结果
[0 0 0 18 0]
求数组的长度
package main
import "fmt"
func main() {
a := [...]int{3: 18, 8: 12} // 给索引为 3 的元素赋值为 18,给索引为 8 的元素赋值为 12
fmt.Println(a)
fmt.Println(len(a)) // 查看数组的长度,因为索引为 8 的元素有值,所以长度最少为 9
}
结果
[0 0 0 18 0 0 0 0 12]
9
使用 for range 循环数组
package main
import "fmt"
func main() {
a := [...]string{"aa", "bb", "cc", "张三", "李四"}
for index, value := range a { // index 为索引,value 为值
fmt.Println(index, value)
}
}
结果
0 aa
1 bb
2 cc
3 张三
4 李四
数组是值类型
package main
import "fmt"
func main() {
a := [3]string{"django", "python", "go"}
b := [5]string{"php", "javascript", "rust", "java", "vue"}
fmt.Printf("%T\n", a)
fmt.Printf("%T\n", b)
}
结果
[3]string
[5]string
如上所示,数组 a 和数组 b 都是数组类型,但是不是同一种类型。从上面的结果可以看到,a 和 b 的长度不一样,所以是不一样的数组类型
在 go 中,数组是值类型,如下
package main
import "fmt"
func printMsg(s [3]string) {
s[0] = "hi" // 修改索引为 0 的值
fmt.Printf("%p\n", &s) // 查看内存地址
fmt.Println(s)
}
func main() {
a := [3]string{"django", "python", "go"}
printMsg(a) // 调用函数
fmt.Printf("%p\n", &a) // 查看内存地址
fmt.Println(a)
}
结果
0xc0000721b0
[hi python go]
0xc000072180
[django python go]
从结果中,我们可以看到,当我们在函数中改变了数组的值后,在函数外面还是原来的值。这是因为在 go 中数组是值类型。在给函数传参的时候,实际上是拷贝了一份数据,可以看出,他们的内存地址是不同的
访问数组元素
- 数组元素可以通过索引(位置)来读取和赋值
- 索引从 0 开始
package main
import "fmt"
func main() {
var arr1 [10]int
// 根据索引赋值
for i := 0; i < 10; i++ {
arr1[i] = i
}
// 根据索引查询数据
for i := 0; i < 10; i++ {
fmt.Println(arr1[i])
}
}
结果
0
1
2
3
4
5
6
7
8
9
数组赋值
package main
import "fmt"
func main() {
var age1 [5]int = [5]int{}
age1[0] = 18 // 索引从 0 开始
age1[1] = 23
age1[2] = 43
age1[3] = 54
age1[4] = 55
//age1[-1]=87 // 错误,数组越界了
//age1[5]=32 // 错误,数组越界了
fmt.Println(age1)
}
循环数组
package main
import "fmt"
func main() {
var age1 [5]int = [5]int{18, 23, 43, 54, 55}
// 第一种循环
for i := 0; i < len(age1); i++ {
fmt.Println(age1[i])
}
// 第二种循环,通过 for range
for i, val := range age1 {
// i 为索引,val 为数组里的值
fmt.Println(i, val)
}
}
结果
18
23
43
54
55
0 18
1 23
2 43
3 54
4 55
指针数组
- 数组的元素除了是某个类型外,还可以是某个类型的指针
- new 函数返回一个 TYPE 类型的数据结构划分内存并执行默认的初始化操作,然后返回这个数据对象的指针
package main
import "fmt"
func main() {
var arr1 [5]*int // 定义了一个数组 arr1,里面有五个 int 类型的指针
//根据索引赋值
arr1[0] = new(int) // new 创建的是一个指针地址
arr1[1] = new(int)
arr1[2] = new(int)
arr1[3] = new(int)
arr1[4] = new(int)
fmt.Println(arr1) // 打印的是指针的内存地址
*arr1[0] = 10 // 给数组 arr1 赋值,因为是指针数组,所以需要在 arr1 前面加上 *
*arr1[1] = 20
fmt.Println(arr1) // 打印的是指针的内存地址,取值需要加 *
fmt.Println(*arr1[1]) // 打印的是具体的值,取值需要加 *
for i := 0; i < len(arr1); i++ {
fmt.Printf("[索引:%d 值是: %d]\n", i, *arr1[i])
}
}
结果
[0xc00001e080 0xc00001e088 0xc00001e090 0xc00001e098 0xc00001e0a0]
[0xc00001e080 0xc00001e088 0xc00001e090 0xc00001e098 0xc00001e0a0]
20
[索引:0 值是: 10]
[索引:1 值是: 20]
[索引:2 值是: 0]
[索引:3 值是: 0]
[索引:4 值是: 0]
数组指针
- 数组是一个值类型,所有的值类型变量在赋值和作为参数传递时都将产生一次复制操作
- 从内存和性能上来看,在函数间传递数组是一个开销很大的操作。因为无论这个数组有多长,都会完整复制,并传递给函数
- 数组指针只需要很小传递
package main
import (
"fmt"
"unsafe"
)
func bigArrPoint(arr *[1e6]int64) { // 参数是一个数组的指针
fmt.Printf("[数组指针复制:大小:%d字节]\n", unsafe.Sizeof(arr))
}
func bigArr(arr [1e6]int64) { // 参数是一个数组
fmt.Printf("[数组复制:大小:%d字节]\n", unsafe.Sizeof(arr))
}
func main() {
var arr [1e6]int64 // 定义了一个 1e6 个长度的数组
bigArr(arr) // 调用 bigArr
bigArrPoint(&arr) //把 arr 的内存地址传给
}
结果
[数组复制:大小:8000000字节]
[数组指针复制:大小:8字节]
多维数组
- 多维数组的典型用例是平面坐标(二维数组)和三维坐标(三维数组)
- Golang 的数组本身只有一个维度,但是我们可以组合多个数组从而创建出多维数组
package main
import (
"fmt"
)
func main() {
// 定义了一个多维数组, [4]表示有四个元素,[2]每个元素里有两个
arr1 := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
fmt.Println(arr1)
fmt.Println(arr1[3][1]) //
}
结果
[[10 11] [20 21] [30 31] [40 41]]
41