数组
数组的声明
package main
import "fmt"
func main() {
// 定义数组
var arr [5]int
// 数组使用下标访问
arr[0] = 1
arr[1] = 2
// 通过下标获取数值对应的值
fmt.Println(arr[0]) // 输出 1
}
数组有长度限制,访问和赋值不能超过数组定义的长度,否则就会下标越界。
fmt.Println("数组的长度为:", len(arr)) // 数组中实际存储的数据量
fmt.Println("数组的容量为:", cap(arr)) // 数组所能存储的最大数据量 因为数组是定长的 所以长度和容量是相同的
数组的创建
arr1 := [3]int{1, 2}
fmt.Println(arr1[2]) // 下标为2的元素默认没有赋值,所以对应该数据类型的零值、
可变数组
不受长度定义限制,以数组实际数据量为准
// 长度可以用...代表,根据数值长度,程序自动填充数组的大小
arr4 := [...]int{1, 2, 3, 4}
fmt.Println(arr4) // 输出 [1 2 3 4]
// 简短声明方式
arr5 := [...]int{2: 3, 6: 3}
fmt.Println(arr5) // 输出 [0 0 3 0 0 0 3] 因为其他下标位置没有设置值,所以默认为0值
遍历数组
使用for range循环数组元素
1. 使用range不需要操作下标,每次循环自动获取元素中的下标和对应的值。达到数组末尾,自动结束循环
arr6 := [5]int{1, 2, 3, 4, 5}
// range循环数组
for index, value := range arr6 {
fmt.Println(index, value)
}
输出
0 1
1 2
2 3
3 4
4 5
2. 通过for循环,配合下标访问数组中的元素
arr7 := [5]int{1, 2, 3, 4, 5}
for i := 0; i < len(arr7); i++ {
fmt.Println(arr7[i])
}
输出
1
2
3
4
5
多维数组
声明多维数组
// 声明一个二维数组
var arr [2][6]int
// 二维数组的赋值
arr[0] = [6]int{1, 2, 3, 4, 5, 6}
// 打印结果
fmt.Println(arr) // 输出 [[1 2 3 4 5 6] [0 0 0 0 0 0]]
// 通过下标赋值
arr[1][0] = 1
fmt.Println(arr) // 输出 [[1 2 3 4 5 6] [1 0 0 0 0 0]]
切片Slise
切片与数组不同,无需指定长度
// 声明一个切片
var slice []int
// 使用make函数来创建
slice = make([]int, 5, 10)
fmt.Println(slice) // 输出 [0 0 0 0 0]
fmt.Println(len(slice), cap(slice)) // 输出 5 10
切片追加元素append()
使用append()函数,将元素追加到切片的末尾
var slice []int
slice = append(slice, 1, 2, 3)
fmt.Println(slice) // 输出 [1 2 3]
// 使用make函数创建切片
s1 := make([]int, 0, 5)
fmt.Println(s1) // 输出 []
s1 = append(s1, 1, 2, 3)
fmt.Println(s1) // 输出 [1 2 3]
// 切片可以扩容,那么,意味着可以一直往后追加,甚至大于5的容量
s1 = append(s1, 4, 5, 6)
fmt.Println(s1) // 输出 [1 2 3 4 5 6]
// 添加一组切片到另一切片当中
s2 := make([]int, 0, 5)
s2 = append(s2, s1...) // 注意这里的...,表示将s1切片中的元素一个个添加到s2中
fmt.Println(s2) // 输出 [1 2 3 4 5 6]
make()与new()的区别
make()主要用来创建并初始化slice切片类型,map字典类型,chnnel通道类型数据。new()主要用于各种数据类型的内存分配,在Go语言中认为他返回的是一个指针,指向的是一个某种类型的零值。make返回的是一个有着初始值的非零值。
// 使用new方法新建切片
slice1 := new([]int)
fmt.Println(slice1) // 输出 &[],这是一个地址
// 使用make方法新建切片
slice2 := make([]int, 0, 5)
fmt.Println(slice2) // 输出 []
fmt.Println(slice1[0]) // 结果出错,是一个空指针 invalid operation: cannot index slice1 (variable of type *[]int)
fmt.Println(slice2[0]) // 输出 0
切片的扩容
s1 := make([]int, 0, 3)
fmt.Printf("地址%p, 长度%d, 容量%d \n", s1, len(s1), cap(s1)) // 地址0xc00000e0c0, 长度0, 容量3
s1 = append(s1, 1, 2, 3)
fmt.Printf("地址%p, 长度%d, 容量%d \n", s1, len(s1), cap(s1)) // 地址0xc00000e0c0, 长度3, 容量3
s1 = append(s1, 4, 5, 6)
fmt.Printf("地址%p, 长度%d, 容量%d \n", s1, len(s1), cap(s1)) // 地址0xc00000e0f0, 长度6, 容量9
切片容量成倍扩充 3 ---> 9 ---> 27...
如果添加数组容量够用,地址不变。如果切片扩展,则会生成一个新地址使用,老地址自动销毁。
切片总结
- 每个切片都引用了一个底层数组。
- 切片本身不存储任何数据,是底层数组存储数据,实际上,修改切片就是修改对应底层数组的数据。
- 当切片添加数据时,没有超过容量,直接添加,超出容量自动扩容,成倍增长。
- 切片一旦扩容,等于执行了一个新的底层数组,数组对应的地址自然也会跟着变化。
切片的删除
删除切片中元素的方法
// 获取切片指定位置的值 重新赋值给当前切片
slice := []int{1, 2, 3, 4, 5}
slice = slice[1:] // 从索引1开始截取,包含索引1
fmt.Println(slice) // 输出 [2 3 4 5]
// 使用append不会改变当前切片的内存地址
slice = append(slice[:0], slice[1:]...) // 从索引1开始截取,包含索引1
fmt.Println(slice) // 输出 [2 3 4 5]
删除指定下标元素
slice := []int{1, 2, 3, 4, 5}
i := 2// 要删除的下标为2
slice = append(slice[:i], slice[i+1:]...) // 从索引1开始截取,包含索引1
fmt.Println(slice) // 输出 [1 2 4 5]
删除切片结尾的方法
slice := []int{1, 2, 3, 4, 5}
slice = slice[:len(slice)-2] // 删除最后2个元素
fmt.Println(slice) // 输出 [1 2 3]