1 数组
数组是相同数据类型的一组数据的集合。
数组定义后,长度不能修改,可以通过**下标(索引)**访问元素。
1.1 数组的语法
var variable_name [SIZE] variable_type
参数说明:
1.variable_name:数组名称
2.SIZE:数组长度,必须是常量
3.variable_type:数组保存元素的类型
func Array() {
// 数组
var a1 [3]int // 定义一个int类型的数组a1,长度是3
var s1 [2]string // 定义一个字符串类型的数组s1,长度是2
var b [4]bool // 定义一个布尔类型的数组b,长度是4
fmt.Printf("a1: %T\n", a1)
fmt.Printf("s1: %T\n", s1)
fmt.Printf("b: %T\n", b)
fmt.Printf("a1: %v\n", a1)
fmt.Printf("s1: %v\n", s1)
fmt.Printf("b: %v\n", b)
}
// 运行结果: a1: [3]int s1: [2]string b: [4]bool a1: [0 0 0] s1: [ ] b: [false false false false]
// 从上面运行结果可以看出,数组和长度和元素类型共同组成了数组的类型。
1.2 数组初始化
初始化,就是给数组的元素赋值,没有初始化的数组,默认元素值都是零值,布尔类型是false,字符串是空字符串。
1.2.1 未初始化的数组
func uninitialized_Array() {
// 未初始化的数组
var a2 [3]int // 定义一个int类型的数组a2,长度是3
var s2 [2]string // 定义一个字符串类型的数组s2,长度是2
var b1 [2]bool
fmt.Printf("a2: %v\n", a2)
fmt.Printf("s2: %v\n", s2)
fmt.Printf("b: %v\n", b1)
}
// 运行结果: a2: [0 0 0] s2: [ ] b1: [false false]
1.2.2 初始化-使用列表
使用初始化列表,就是将值写在大括号里面。
func initialized_Array() {
// 使用初始化列表
var a3 = [5]int{1, 6, 7, 8, 90}
var s3 = [2]string{"小罗", "https://linuxtech.top/"}
var b2 = [2]bool{true, false}
fmt.Printf("a3: %v\n", a3)
fmt.Printf("s3: %v\n", s3)
fmt.Printf("b2: %v\n", b2)
a4 := [3]int{67, 98, 456} // 类型推断
fmt.Printf("a4: %v\n", a4)
}
// 运行结果: a3: [1 6 7 8 90] s3: [小罗 https://linuxtech.top/] b2: [true false] a4: [67 98 456]
1.2.3 初始化-省略数组长度
数组长度可以省略,使用...代替,根据初始化值的数量自动推断。
func nulen_Array() {
// 省略数组长度---初始化
var a4 = [...]int{34, 78, 67, 21}
var s4 = [...]string{"小罗", "https://linuxtech.top/"}
var b3 = [...]bool{true, false}
a5 := [...]int{7, 5, 6, 3, 2, 1, 0}
fmt.Printf("a4: %v\n", a4)
fmt.Printf("s4: %v\n", s4)
fmt.Printf("b3: %v\n", b3)
fmt.Printf("a5: %v\n", a5)
fmt.Printf("len(a4): %v\n", len(a4)) // 数组长度
}
// 运行结果: a4: [34 78 67 21] s4: [小罗 https://linuxtech.top/] b3: [true false] a5: [7 5 6 3 2 1 0] len(a4): 4
1.2.4 初始化-指定索引值
可通过指定所有的方式来初始化,未指定所有的默认未零值。
func indexValue_Array() {
// 初始化---指定索引值
var a6 = [...]int{0: 1, 2: 6}
var s5 = [...]string{1: "小罗", 2: "https://linuxtech.top/"}
var b4 = [...]bool{2: true, 6: false}
fmt.Printf("a6: %v\n", a6)
fmt.Printf("s5: %v\n", s5)
fmt.Printf("b4: %v\n", b4)
}
// 运行结果:a6: [1 0 6] s5: [ 小罗 https://linuxtech.top/] b4: [false false true false false false false]
1.3 修改数组
func edit_array() {
// 修改数组的元素-直接下标修改
var a7 = [...]int{78, 79, 80}
fmt.Printf("a7: %v\n", a7)
a7[1] = 90
fmt.Printf("a7: %v\n", a7)
}
1.4 访问数组元素
可通过下标的方式来访问数组元素。数组的最大下标为数组长度-1,大于这个下标会发生数组越界。
func access_index_array() {
var a [2]int // 数组长度是2,最大下标是1
a[0] = 100
a[1] = 200
fmt.Printf("a[0]: %v\n", a[0])
fmt.Printf("a[1]: %v\n", a[1])
// 修改a[0]、a[1]
a[0] = 1
a[1] = 2
fmt.Printf("a[0]: %v\n", a[0])
fmt.Printf("a[1]: %v\n", a[1])
// 数组长度越界 a[3]=1000
}
1.4.1 数组长度遍历数组
可根据数组长度,通过for循环的方式遍历数组,数组的长度可以使用len函数获得。
func len_array() {
// 根据数组的长度和下标遍历数组
a1 := [...]int{1, 5, 7, 0, 3, 4}
/*
fmt.Printf Go标准库的格式化打印函数,可以自定义输出文字格式。
"a1[%v]:%v\n" 格式化字符串
%v:Go里的通用占位符,会自动匹配变量的值格式来输出
第一个%v:用来填充下标i
第二个%v:用来填充数组元素a1[i]
i 循环里的索引(下标),从0一直递增到数组最大长度-1
a1[i] 通过下标i 访问数组a1里对应的元素(就是你刚才问的「访问索引」用法)
*/
for i := 0; i < len(a1); i++ { // i < len(a1) 等同于 i <= len(a1)-1
fmt.Printf("a1[%v]:%v\n", i, a1[i])
}
}
1.4.2 for range遍历数组
可以使用for range循环遍历数组,range返回数组下标和对应的值
func for_range_array() {
// 使用for range循环遍历数组
a2 := [...]int{2, 4, 6, 8, 10}
for i, v := range a2 {
fmt.Printf("a2[%v]: %v\n", i, v)
}
}
2 切片
数组是固定长度,可以容纳相同数据类型的元素的集合。当长度固定时,使用还是带来一些限制,比如:申请的长度太大浪费内存,太小又不够用。
切片是可变长度的数组,底层就是使用数组实现的,增加了自动扩容功能。
切片(Slice)是一个拥有相同类型元素的可变长度的序列。
2.1 语法
1.声明切片和声明数组类似,只要不添加长度就可以
var identifier []type
2.切片是引用类型,可以使用`make`函数创建切片
var slice1 []type = make([]type, len)
// 也可以简写为:
slice1 := make([]type, len)
3.也可以指定容量,其中capacity为可选参数。
make([]T, length, capacity)
注明:`len`是数组的长度,并且也是切片的初始长度。
func slice() {
// 切片示例-声明
// 这种声明的方式还没分配内存
var names []string
var numbers []int
fmt.Printf("names: %v\n", names)
fmt.Printf("numbers: %v\n", numbers)
}
func make_slice() {
// 通过make声明切片--make的方式已经分配了内存,只不过没赋值
var s = make([]int, 2)
fmt.Printf("s: %v\n", s)
}
2.2 切片的长度和容量
切片拥有自己的长度和容量,可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。
func len_cap_slice() {
// 切片的长度和容量
var Names = []string{"小罗", "https://linuxtech.top/"}
var Numbers = []int{4, 7, 9, 0}
fmt.Printf("len(Names): %d cap(Names):%d \n", len(Names), cap(Names))
fmt.Printf("len(Numbers): %d cap(Numbers):%d \n", len(Numbers), cap(Numbers))
var s1 = make([]string, 56, 78)
fmt.Printf("len(s1): %d cap(s1):%d \n", len(s1), cap(s1))
}
2.3 切片初始化
切片可以直接初始化,也可以使用数组初始化。
2.3.1 切片的切分
func slice() {
// 切片的切分
var s = []int{2, 6, 7, 8, 9, 0} // 索引: 0 1 2 3 4 5
s1 := s[0:3] // [)---前闭后开,不包含3
fmt.Printf("s1[0:3]: %v\n", s1)
s2 := s[3:]
fmt.Printf("s2[3:]: %v\n", s2)
s3 := s[2:5]
fmt.Printf("s3[2:5]: %v\n", s3)
s4 := s[:]
fmt.Printf("s4[:]: %v\n", s4)
}
2.3.2 直接初始化
func slice_init() {
// 直接初始化
s_init := []int{3, 6, 9, 0}
fmt.Printf("s_init: %v\n", s_init)
}
2.3.3 使用数组初始化
func slice_array_init() {
// 使用数组初始化
array_init := [...]int{2, 4, 6, 8, 10, 9}
slice_array_init1 := array_init[:3] // [2 4 6]
slice_array_init2 := array_init[:3] // [2 4 6]
slice_array_init_all := array_init[:] // 取数组中的所有元素
fmt.Printf("slice_array_init1: %v\n", slice_array_init1)
fmt.Printf("slice_array_init2: %v\n", slice_array_init2)
fmt.Printf("slice_array_init_all: %v\n", slice_array_init_all)
}
2.3.4 切片表达式
使用数组的部分元素初始化(切片表达式)
切片的底层就是一个数组,所以可以基于数组通过切片表达式得到切片。
切片表达式中的low和high表示一个索引范围(左包含,右不包含),得到的切片长度=high-low,容量等于得到切片的底层数组的容量。
func slice_init_array_element() {
// 使用数组的部分元素初始化(切片表达式)
Array := [...]int{34, 35, 36, 37, 38, 39}
s1_Array := Array[2:5]
fmt.Printf("s1_Array: %v\n", s1_Array)
s2_Array := Array[2:]
fmt.Printf("s2_Array: %v\n", s2_Array)
s3_Array := Array[:3]
fmt.Printf("s3_Array: %v\n", s3_Array)
}
2.4 空(nil)切片
一个切片在未初始化之前默认为 nil,长度为 0,容量为0.
func nil_slice() {
var nilSlice []int
fmt.Println(nilSlice == nil)
fmt.Printf("len(nilSlice):%d,cap(nilSlice):%d \n", len(nilSlice), cap(nilSlice))
}
// 运行结果: true len(nilSlice):0,cap(nilSlice):0
2.5 切片的遍历
切片的遍历和数组的遍历类似,可使用for循环索引遍历,或者for range循环。
2.5.1 for循环索引
func index_slice() {
// for循环索引遍历
indexSlice := []int{2, 4, 6, 8, 0} // var indexSlice = []int{2, 4, 6, 8, 0}
sliceLength := len(indexSlice)
/* for i := 0; i < sliceLength; i++ {
fmt.Printf("indexSlice[%v]:%v\n", i, indexSlice[i])
} */
for i := 0; i <= sliceLength-1; i++ { // 快捷键 fori...
fmt.Printf("indexSlice[%v]:%v\n", i, indexSlice[i]) // 通过下标取值---indexSlice[i]
}
}
2.5.2 for range循环
func range_slice() {
// for range循环遍历
rangeSlice := []int{1, 3, 5, 7, 9}
for i, v := range rangeSlice { // 快捷键forr...
fmt.Printf("rangeSlice[%v]:%v\n", i, v)
}
}
2.6 操作切片元素
切片是一个动态数组,可使用append()函数添加元素,Go语言中没有删除切片元素的专用方法,可以使用切片本身的特性删除元素。
切片是引用类型,通过赋值的方式,会修改原有内容,Go提供了copy()函数来拷贝切片。
切片可以模拟数据库的增删改查,即db CRUD
2.6.1 添加元素
func sliceAppend() {
// 添加元素
s_append := []int{}
s_append = append(s_append, 1)
s_append = append(s_append, 3)
s_append = append(s_append, 5, 7, 9) // 添加多个元素
fmt.Printf("s_append: %v\n", s_append)
s := []int{11, 13, 15}
s_append = append(s_append, s...)
fmt.Printf("s_append: %v\n", s_append) // 添加另外一个切片
}
2.6.2 删除元素
从切片Slice中删除索引为index的元素,操作方法是Slice = append(Slice[:index], Slice[index+1:]...)
核心原理:新建了一个切片,重新添加元素
func sliceDelete() {
// 删除元素
s_delete := []int{90, 91, 92, 93, 94, 95}
s_delete = append(s_delete[:2], s_delete[3:]...) // 删除索引为2的元素
fmt.Printf("s_delete: %v\n", s_delete)
}
2.6.3 修改元素
func modify_slice() {
// 修改元素
s_modify := []int{3, 5, 7, 0, 11}
s_modify[2] = 9
fmt.Printf("s_modify: %v\n", s_modify)
}
2.6.4 查询元素
func query_slice() {
// 查询元素
var s_query = []int{5, 8, 9, 12, 13, 45}
var key = 13
for i, v := range s_query {
if v == key {
fmt.Printf("s_query[%v]:%v\n", i, s_query[i])
fmt.Printf("i:%d,s_query[i]:%d\n", i, s_query[i])
}
}
}
2.6.5 拷贝切片
func copySlice() {
// 拷贝元素
s1 := []string{"小罗", "哈尔滨工业大学", "北京大学"}
s2 := s1 // 赋值时相当于是把内存地址给S2,所以修改s2时,s1也被修改了
s2[0] = "西安交通大学"
fmt.Printf("s1: %v\n", s1)
fmt.Printf("s2: %v\n", s2)
fmt.Println("---------------------")
s3 := []string{"小罗", "哈尔滨工业大学", "北京大学"}
s4 := make([]string, 3) // 类型为字符串,分类内存为3
copy(s4, s3) // copy后,再修改时,s3未被修改
s4[0] = "西北工业大学"
fmt.Printf("s3: %v\n", s3)
fmt.Printf("s4: %v\n", s4)
}
// 赋值的情况下,原来的变量被修改了,使用copy函数,原来的变量没有被修改。