Go语言数组数组
一维数组
- 数组定义格式:
var arr [3]int
- 数组的初始化方式
- 先定义后初始化
注意点:
Go语言数组支持先定义后一次性初始化
//1.先定义后初始化 var nums [3]int //先定义数组再逐个初始化 nums[0] = 1 nums[1] = 2 nums[2] = 3 //先定义数组再一次性初始化 nums = [3]int{2,3,4} fmt.Println(nums)
- 定义的同时初始化
-
注意点:
给数组一次性赋值的时候一定要在值的前面加上数组的类型,而且必须要和数据类型一模一样 - C语言中数组没有初始化保存的是垃圾数据,Go语言中数组没有初始化保存的是零值
-
//var arr [3]int = [3]int{2, 3, 4} //定义的同时部分初始化,未初始话的元素为0 var arr [3]int = [3]int{1,3} //指定元素初始化 var arr [3]int = [3]int{2:4} //[...]的含义, 让编译器根据赋值的元素个数自动计算数组的元素个数 arr := [...]int{1,3,4} fmt.Println(arr) fmt.Println(arr[0]) fmt.Println(arr[1]) fmt.Println(arr[2])
- 先定义后初始化
零值
- 什么是零值
- 数据类型未初始化,系统自动添加的数据
- 各种数据类型零值
- int/int8/int16/int32/int64/uint/uint8/uint16/uint32/uint64/byte/rune/uintptr的默认值是0
- float32/float64的默认值是0.0
- bool的默认值是false
- string的默认值是""
- pointer/function/interface/slice/channel/map/error的默认值是nil
值类型
- Go语言在数组中是值类型,所以数组之间的赋值是值拷贝关系,而不是指向关系
- 如果想让两个数组可以相互赋值, 那么两个数组的类型必须一致类型一致包括元素个数, 元素个数也是Go语言中数组类型的一部分
- 如果数组中存储的元素类型支持== != 操作,而且两个数组的类型是一个样的, 那么数组也支持== !=操作
package main
import "fmt"
func main() {
nums := [...]int {2,3,4}
arr := [...]int{2,3,4}
fmt.Println(nums == arr) //true
nums := [3]int{1,2,3}
res := nums
fmt.Println(res) //[1,2,3]
nums := [3]int{1,2,3}
change(nums)
fmt.Println(nums) //[1,2,3]
}
//函数内部无法改变数组元素的值
func change(nums [3]int) {
nums[2] = 666
}
二维数组
- Go语言二维数组格式:
var 数组名称[一维数组个数][一维数组元素个数]数组类型
package main
func main() {
//定义二维数组格式
var nums [2][3]int = [2][3]int{
{1,3,5},
{2,5,6},
}
//简单定义方法
nums := [2][3]int{
{1,2,3},
{4,5,6},
}
arr := [...][3]int{
{12,3,5},
{1,2,6},
}
fmt.Println(arr)
//二维数组也可以省略元素个数,但是只可以省略行数,不可以省略列数
arr := [...][3]int{
{12,3,5},
{1,2,6},
}
//遍历二维数组
for i:=0; i< 2; i++{
for j:=0;j < 3 ;j++ {
fmt.Println(arr[i][j])
}
}
}
数组的遍历
- Go语言数组遍历两种方法
- 普通for循环遍历
nums := [...]int{2, 3, 5, 1, 3, 6} //普通for循环遍历 for i := 0; i < 6; i++ { fmt.Println("index= ", i, "value = ", nums[i]) }
- for..range遍历
nums := [...]int{2, 3, 5, 1, 3, 6} //高级for循环遍历 for key,value := range nums{ fmt.Println(key,"-->",value) }
Go语言切片
切片
-
什么是切片
- 在Go语言中,数组的长度一旦确定就无法改变,为了解决这一问题,推出了一种数据类型切片
- 切片简单理解就是一个可变长度的数组,底层的实现原理就是一个结构体, 结构体中有一个指针指向了一个数组
本质上所有的数据都是保存在指向的数组中的
-
创建切片
- 通过数组创建切片1
通过数组来创建 格式: [起始位置:结束位置], 从起始位置开始截取, 直到结束位置, 但是不包括结束位置 注意: 截取了多少个元素, len就等于几 容量等于数组的长度 - 起始位置 nums := [5]int{1,2,3,4,5} //1.通过数组创建切片 //1.1[起始位置 : 结束位置] 从起始位置开始截取,直到结束位置,但是不包括结束位置索引对应的元素 var sce []int = nums[2 : 4] //1.2[: 结束位置]只指定结束位置,不指定起始位置,就是从开始位置截取直到指定的位置,但是也不包括指定位置的元素 var sce []int = nums[:3] //1.3[起始位置 : ] 从起始位置开始截取,直到末尾 var sce []int = nums[0:] //1.4[: ]只有一个:,从开始位置截取,直到末尾结束 var sce []int = nums[:] fmt.Println(sce) //计算切片的长度len当前保存的数据个数 fmt.Println(len(sce)) //计算切片的容量cap总共能够保存数据的个数 fmt.Println(cap(sce))
- 通过数组创建切片2
切片的第三个参数max,不能小于第二个参数,作用是用来控制切片容量的
//如果指定了第三个参数, 那么切片的容量就等于 第三个参数 - 第一个参数 //注意点: 如果没有指定第三个参数,那么切片的容量 = 数组的容量 - 第一个参数值 // 如果指定了第三个参数,那么切片的容量 = 第三个参数 - 第一个参数值 arr := [7]int{1,2,3,4,5,6,7} sce := arr[1:4:4] fmt.Println(sce) fmt.Println(len(sce)) fmt.Println(cap(sce))
- 通过make()函数创建切片
// 2.通过make函数创建切片 // 第一个参数: 告诉系统要存储什么类型的数据 // 注意点: 如果是创建切片一定要在传入的数据类型前面写上[] // 第二个参数: 告诉系统创建出来的切片len等于多少 // 第三个参数: 告诉系统创建出来的切片cap等于多少 // 注意点: 第三个参数可以省略, 如果省略切片的容量就等于切片的长度(等于第二个参数) //var sce []int = make([]int, 2, 5) var sce []int = make([]int, 2) fmt.Println(sce) fmt.Println(len(sce)) fmt.Println(cap(sce))
- 通过语法糖创建切片
// 3.通过Go提供的语法糖来创建 // 一定要注意[]里面没有值就是切片 // 通过Go提供的语法糖来创建len等于cap var sce []int = []int{1, 3, 5} // 相当于make([]int, 3) fmt.Println(sce) fmt.Println(len(sce)) fmt.Println(cap(sce))
切片与数组
- [ ]没有数字就是切片,有数字就是数组,[...]这种定义格式是数组
//定义数组
var arr [5]int = [5]int{1,3,4,5,6}
var arr = [5]int{1,3,4,5,6}
arr := [5]int{1,3,4,5,6}
arr := [...]int{1,3,4,5,6}
//定义二维数组
var arrs [2][3]int = [2][3]int{
{1,3,5},
{2,4,6},
}
var arrs = [2][3]int{
{1,3,5},
{2,4,6},
}
arrs := [2][3]int{
{1,3,5},
{2,4,6},
}
arrs := [...][3]int{
{1,3,5},
{2,4,6},
}
//定义切片
var sce []int = []int{1,3,5}
var sce = []int{1,3,5}
sce := []int{1,3,5}
//定义一个切片,切片中保存的是数组
var sec [][3]int = [][3]int{
{1, 3, 5},
{2, 4, 6},
}
var sec = [][3]int{
{1, 3, 5},
{2, 4, 6},
}
sec := [][3]int{
{1, 3, 5},
{2, 4, 6},
}
//定义一个切片,切片中保存切片
var sec[][]int = [][]int {
{1, 3, 5},
{2, 4, 6},
}
var sec = [][]int {
{1, 3, 5},
{2, 4, 6},
}
sec := [][]int {
{1, 3, 5},
{2, 4, 6},
}
append函数
- append函数格式 :
append(切片,数据)
- append函数作用: 追加切片中的数据
- append函数注意点
- 如果通过append函数追加数据之后超过了原有的容量, 那么系统内部会自动按照当前容量*2的方式重新定义一个数组作为切片保存数据的模型
- 在扩容的时候会重新定义一个新的切片, 所以需要返回一个切片
- 扩容问题
- 容量小于1024的时候扩容, 会按照原有容量的2倍扩容
- 容量大于1024的时候扩容,会按照原有的1/4进行扩容
sec := make([]int, 1024, 1024)
sec = append(sec, 6)
fmt.Println(len(sec))
fmt.Println(cap(sec))//1280
切片的使用
- 定义切片
sec := []int{1,3,5,7,9,11}
- 修改切片中的数据
sec[2] = 666
- 增加切片中的数据
sec = append(sec, 7)
fmt.Println(sec)
- 删除切片中指定的数据
//5.删除切片中的指定索引对应的数据
index := 3
//不仅可以利用数组获取切片,还可以用切片获取切片
//sec = sec[:index]
//fmt.Println(sec) //[1 3 5]
//sec = sec[index +1 :]
//fmt.Println(sec) //[9 11]
//将第二个切片追加到第一个切片后面即可
sec = append(sec[:index], sec[index +1 :]...)
fmt.Println(sec) //[1 3 5 9 11]
切片的内存地址
package main
import "fmt"
func main() {
/*
通过切片生成切片的注意点
*/
//1.创建一个数组
var arr = [3]int{1,2,3}
//Go语言中不能通过数组名称获取数组的地址
//fmt.Printf("%p\n", arr)
fmt.Printf("%p\n", &arr) //0xc0420500a0
fmt.Printf("%p\n", &arr[0]) //0xc0420500a0
//2.通过数组创建一个切片
//直接打印sce打印的是sce中指针保存的地址,也就是底层指向的那个数组的地址
sce := arr[:]
fmt.Printf("%p\n", sce) //0xc0420500a0
//3.通过切片创建切片
//通过切片创建一个切片, 新的切片和老的切片底层指向同一个数组
sce2 := sce[:]
fmt.Printf("%p\n", sce2) //0xc0420500a0
//修改数组中的值
arr[2] = 666
sce[2] = 666
sce2[2] = 666
fmt.Println(arr)
fmt.Println(sce)
fmt.Println(sce2)
}
copy函数
package main
import "fmt"
func main() {
/*
copy函数的作用是将源切片的值拷贝到目标切片当中
函数格式 copy(目标切片, 源切片)
*/
sec1 := []int{1, 2, 3, 6, 9, 10}
sec2 := make([]int, 2, 5)
fmt.Println(sec2)
//将源切片的值拷贝到目标切片当中
//注意点: 拷贝的时候是以目标切片为准,目标切片中长度为多少将来只能拷贝多少个值进来
copy(sec2, sec1)
fmt.Println(sec2)
}
切片与字符串
注意点: 切片和数组不同, 切片不支持== !=操作
package main
import "fmt"
func main() {
/*
1.切片可以再生成新的切片, 两个切片底层指向同一个数组
2.切片和数组不同, 切片不支持== !=操作
3.在Go语言中字符串的底层实现就是切片, 所以可以通过字符串来生成切片
*/
//数组可以使用== !=操作
//arr := [3]int{1,2,3}
//arr2 := [3]int{4,5,6}
//arr = arr2
//fmt.Println(arr)
var str string = "www.it666.com"
sec := make([]byte, len(str))
copy(sec,str)
fmt.Println(len(str))
fmt.Printf("%s\n",sec) //www.it666.com
}
切片与数组注意点
数组定义后不初始化,可以使用,切片定义后可以一次性初始化,但是不能逐个初始化,但是可以使用append函数添加数据
- 数组定义后直接使用
var arr [4]int
//数组定义后可以直接使用
arr[0] = 1
arr[1] = 3
arr[2] = 5
arr[3] = 7
fmt.Println(arr)
- 切片定义后不能直接使用
var sce []int
//不能逐个初始化,会报错
//sce[0] = 1
//sce[1] = 3
//sce[2] = 5
//可以使用append函数添加数据
sce = append(sce,1)
sce = append(sce,3)
sce = append(sce,5)
sce = append(sce,7)
sce = append(sce,9)
//打印切片
fmt.Println(sce)
fmt.Println(len(sce))
fmt.Println(cap(sce))
- 计算数组的长度方式
//计算数组长度的方式
//第一种方式(推荐)使用len函数
arr := [4]int{1,3,5,7}
//length := len(arr)
//fmt.Println(length)
//第二种方式,使用unsafe.Sizeof函数计算(不推荐)
length1 := unsafe.Sizeof(arr)
length2 := unsafe.Sizeof(arr[0])
fmt.Println(length1 / length2)
- 计算切片的长度
//计算切片的长度
//第一种方式(推荐)使用len函数
//注意点: 切片的长度不能使用unsafe.Sizeof函数计算
//由于切片本质是结构体,所以unsafe.Sizeof计算的是结构体的字节数,不是指向数组的字节数
sce := []int{1,3,5,6}
length := len(sce)
fmt.Println(length)
- 切片与数组的传递
- 数组传递是值传递
- 切片传递是地址(指针)传递
package main
import "fmt"
func main() {
/*
1.数组作为函数参数是值传递,在函数内部修改形参是不会影响外部实参的值
2.切片作为函数的参数是地址传递(指针), 在函数内修改形参, 会影响到函数外的实参
*/
//arr := [4]int{1,3,4,5}
//fmt.Println(arr)
//change1(arr)
//fmt.Println(arr)
sce := []int{1,2,3,4}
fmt.Println(sce)
change2(sce)
fmt.Println(sce)
}
//数组是值传递
func change1(arr [4]int) {
arr[0] = 666
}
//切片是地址传递指针
func change2(sce []int) {
sce[0] = 666
}
可变参数
- 可变参数底层实现就是切片
package main
import "fmt"
func main() {
/*
可变参数,底层其实就是一个切片
*/
res := sum(10,20,30)
fmt.Println(res)
}
//可变参数底层就是一个切片
//注意点: 当一个函数中定义可变参数时候,可变参数只能放在形参列表的最后面
func sum(nums ...int) (res int) {
for _, value := range nums {
res += value
}
return
}