Golang学习历程【第五篇 复合数据类型:数组切片】
Golang学习历程【第五篇 复合数据类型:数组&切片】
- 1. 数组(Array)
- 1.1 数组的定义
- 1.2 初始化数组
- 1.3 数据的循环遍历
- 1.4 多维数组
- 2. 切片(Slice)
- 2.1 切片声明、初始化
- 2.2 基于数组创建切片
- 2.2 切片的长度(len)和容量(cap)*
- 3. nil值
- 4. 值类型和引用数据类型
- 5. 切片常用函数
- 5.1 创建切片:make
- 5.2 扩容切片:append
- 5.3 复制切片【深克隆】:copy
- 5.4 切片的删除
- 5.5 sort排序
- 5.5.1 升序排序
- 5.5.2 降序排序
- 6. 扩展技巧
- 6.1 语法提示
- 6.2 方法参数、含义使用探究
1. 数组(Array)
数组: 一系列固定长度、同一类型数据的集合。数组为值类型
1.1 数组的定义
var 变量名 = [元素个数]<数据类型>
// 声明 + 初始化,此时可将[元素个数]替换为[...],表示初始化任意多个数据
var 变量名 = [元素个数]<数据类型>{}
变量名:= [元素个数]<数据类型>{}
示例:
// 数组的定义
var arr1 [4]int
// len获取数据长度
fmt.Printf("类型:%T; \t数组长度:%v; \t数据:%v\n", arr1, len(arr1), arr1)
结果:
类型:[4]int; 数组长度:4; 数据:[0 0 0 0]
1.2 初始化数组
// 数组的定义
var arr1 [4]int
// len获取数据长度
fmt.Printf("类型:%T; \t数组长度:%v; \t数据:%v\n", arr1, len(arr1), arr1)
/* 数组初始化 */
// 1.方式一
arr1[0] = 1
arr1[3] = 4
fmt.Printf("方式一: 类型:%T; \t数组长度:%v; \t数据:%v\n", arr1, len(arr1), arr1)
// 2. 方式二 var 变量名 = [元素个数]<数据类型>{}
var arr2 = [3]float64{1, 2, 4.6}
fmt.Printf("方式二: 类型:%T; \t数组长度:%v; \t数据:%v\n", arr2, len(arr2), arr2)
// 3. 方式三 变量名:= [元素个数]<数据类型>{}
arr3 := [3]string{"java", "Golang", "Python"}
fmt.Printf("方式三: 类型:%T; \t数组长度:%v; \t数据:%v\n", arr3, len(arr3), arr3)
// 4. 方式四 var 变量名 = [...]<数据类型>{} 或 变量名:= [...]<数据类型>{}
arr4 := [...]int{2, 4, 6, 7, 8}
fmt.Printf("方式四: 类型:%T; \t数组长度:%v; \t数据:%v\n", arr4, len(arr4), arr4)
// 5. 方式五 指定下标索引初始化索引
arr5 := [...]float64{0: 2, 2: 3.5, 5: 4.6, 7: 9}
fmt.Printf("方式五: 类型:%T; \t数组长度:%v; \t数据:%v\n", arr5, len(arr5), arr5)
结果:
类型:[4]int; 数组长度:4; 数据:[0 0 0 0]
方式一: 类型:[4]int; 数组长度:4; 数据:[1 0 0 4]
方式二: 类型:[3]float64; 数组长度:3; 数据:[1 2 4.6]
方式三: 类型:[3]string; 数组长度:3; 数据:[java Golang Python]
方式四: 类型:[5]int; 数组长度:5; 数据:[2 4 6 7 8]
方式五: 类型:[8]float64; 数组长度:8; 数据:[2 0 3.5 0 0 4.6 0 9]
1.3 数据的循环遍历
/* 数组的循环遍历 */
arr6 := [3]string{"java", "Golang", "Python"}
// 1. 使用fori循环
fmt.Printf("fori循环 ")
for i := 0; i < len(arr6); i++ {
fmt.Printf("下标%v: 值:%v; \t", i, arr6[i])
}
fmt.Println()
// 2. 使用for range
fmt.Printf("for range循环 ")
for index, value := range arr6 {
fmt.Printf("下标%v: 值:%v; \t", index, value)
}
结果:
fori循环 下标0: 值:java; 下标1: 值:Golang; 下标2: 值:Python;
for range循环 下标0: 值:java; 下标1: 值:Golang; 下标2: 值:Python;
1.4 多维数组
// 2维数组为例 var 变量名 = [行个数][列个数]数据类型{{}}
var arr7 = [3][3]int{
{1, 2, 3}, // 每一行数据
{4, 5, 6},
{7, 8, 9},
}
fmt.Printf("类型:%T; \t数组长度:%v; \t数据:%v\n", arr7, len(arr7), arr7)
// 可以忽略行的数据个数,列的数据个数不允许忽略
var arr8 = [...][2]string{
{"a", "b"},
{"c", "d"},
}
fmt.Printf("类型:%T; \t数组长度:%v; \t数据:%v\n", arr8, len(arr8), arr8)
// 二维数组循环
for i := 0; i < len(arr7); i++ {
for j := 0; j < len(arr7[i]); j++ {
fmt.Printf("%v行%v列: 值:%v; \t", i+1, j+1, arr7[i][j])
}
fmt.Println()
}
for row, rowValue := range arr8 {
for col, value := range rowValue {
fmt.Printf("%v行%v列: 值:%v; \t", row+1, col+1, value)
}
fmt.Println()
}
结果:
类型:[3][3]int; 数组长度:3; 数据:[[1 2 3] [4 5 6] [7 8 9]]
类型:[2][2]string; 数组长度:2; 数据:[[a b] [c d]]
1行1列: 值:1; 1行2列: 值:2; 1行3列: 值:3;
2行1列: 值:4; 2行2列: 值:5; 2行3列: 值:6;
3行1列: 值:7; 3行2列: 值:8; 3行3列: 值:9;
1行1列: 值:a; 1行2列: 值:b;
2行1列: 值:c; 2行2列: 值:d;
2. 切片(Slice)
切片:基于数组实现的一层封装,可变长度的序列,可自动扩容。切片为引用类型
2.1 切片声明、初始化
var 变量名 = []<数据类型>
// 声明 + 初始化
var 变量名 = []<数据类型>{}
变量名:= []<数据类型>{}
示例:
// 切片的定义
var arr1 []int
// len获取数据长度
fmt.Printf("类型:%T; \t切片长度:%v; \t数据:%v\n", arr1, len(arr1), arr1)
/* 切片初始化 */
// 1. 方式一 var 变量名 = []<数据类型>{}
var arr2 = []float64{1, 2, 4.6}
fmt.Printf("方式一: 类型:%T; \t切片长度:%v; \t数据:%v\n", arr2, len(arr2), arr2)
// 2. 方式二 变量名:= []<数据类型>{}
arr3 := []string{"java", "Golang", "Python"}
fmt.Printf("方式二: 类型:%T; \t切片长度:%v; \t数据:%v\n", arr3, len(arr3), arr3)
// 3. 方式三 var 变量名 = []<数据类型>{} 或 变量名:= []<数据类型>{}
arr4 := []int{2, 4, 6, 7, 8}
fmt.Printf("方式三: 类型:%T; \t切片长度:%v; \t数据:%v\n", arr4, len(arr4), arr4)
// 4. 方式四 指定下标索引初始化索引
arr5 := []float64{0: 2, 2: 3.5, 5: 4.6, 7: 9}
fmt.Printf("方式四: 类型:%T; \t切片长度:%v; \t数据:%v\n", arr5, len(arr5), arr5)
// 5. 切片的循环和数组一致,这里不例举
结果:
类型:[]int; 切片长度:0; 数据:[]
方式一: 类型:[]float64; 切片长度:3; 数据:[1 2 4.6]
方式二: 类型:[]string; 切片长度:3; 数据:[java Golang Python]
方式三: 类型:[]int; 切片长度:5; 数据:[2 4 6 7 8]
方式四: 类型:[]float64; 切片长度:8; 数据:[2 0 3.5 0 0 4.6 0 9]
2.2 基于数组创建切片
示例:
// 基于数组定义切片
var arr1 = [5]int{10, 29, 49, 58, 32}
fmt.Printf("类型:%T; \t长度:%v; \t数据:%v\n", arr1, len(arr1), arr1)
// 1. 切片获取,arr1[:]获取arr1中所有的数据
s1 := arr1[:]
// 切片数据类型为[]int,数组数据类型为[5]int
fmt.Printf("获取所有数据--类型:%T; \t长度:%v; \t数据:%v\n", s1, len(s1), s1)
// 2. 切片获取,arr1[2:]获取arr1中从下标为2开始之后所有的数据【包含下标2】
s2 := arr1[2:]
fmt.Printf("获取下标为2开始之后所有的数据--类型:%T; \t长度:%v; \t数据:%v\n", s1, len(s2), s2)
// 3. 切片获取,arr1[:2]获取arr1中从下标为2之前所有的数据【不包含下标2】
s3 := arr1[:2]
fmt.Printf("获取下标为2之前所有的数据--类型:%T; \t长度:%v; \t数据:%v\n", s3, len(s3), s3)
结果:
类型:[5]int; 长度:5; 数据:[10 29 49 58 32]
获取所有数据--类型:[]int; 长度:5; 数据:[10 29 49 58 32]
获取下标为2开始之后所有的数据--类型:[]int; 长度:3; 数据:[49 58 32]
获取下标为2之前所有的数据--类型:[]int; 长度:2; 数据:[10 29]
总结:数据的拆分:arr[start:end] 中,含义为:获取arr对应下标区间[start,end)的值,start,end可省略,表示其前或后的所有值。
2.2 切片的长度(len)和容量(cap)*
切片拥有自己的长度和容量:
长度:切片的长度是切片所包含的元素个数。用**len()函数获取
容量:切片的容量从他第一个元素开始,到底层数组元素末尾的个数。用cap()**函数获取
a := []int{2, 3, 4, 5, 6, 7}
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", a, len(a), cap(a), a)
b := a[3:]
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", b, len(b), cap(b), b)
c := a[:3]
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", c, len(c), cap(c), c)
结果:
类型:[]int, 长度:6, 容量:6, 数据:[2 3 4 5 6 7]
类型:[]int, 长度:3, 容量:3, 数据:[5 6 7]
类型:[]int, 长度:3, 容量:6, 数据:[2 3 4]
3. nil值
nil是一个预先声明的标识符,表示指针、通道、函数、接口、集合或切片类型的零值。
数据类型 | 声明默认值 |
---|---|
bool | |
int,float | 0 |
string | "" |
指针(pointers) | nil |
数组(array) | 基本类型默认值的数组 |
切片(slices) | nil |
集合(maps) | nil |
通道(channels) | nil |
函数(function) | nil |
// 数组默认值
var arr [3]int
fmt.Println(arr)
// 切片默认值
var slices1 []int
var slices2 = []int{1}
fmt.Println(slices1, slices1 == nil, slices2 == nil)
结果:
[0 0 0]
[] true false
4. 值类型和引用数据类型
值类型: 值类型赋值的两个变量为独立个体,A变化不会引起B的同步更新
引用数据类型:引用数据类型赋值的两个变量为引用同一个变量,A变化会引起B的变化。实际上是因为指向同一片内存空间。典型的示例即:切片
示例:
a1 := [...]int{2, 3, 4, 5} // 数组 值复制
a2 := []int{2, 3, 4, 5} // 切片 引用赋值
b1 := a1 // 赋值给b1
b2 := a2 // 赋值给b2
b1[0] = 6 // 同样修改第一个元素值为6
b2[0] = 6 // 同样修改第一个元素值为6
fmt.Printf("a1=%v,b1=%v \n", a1, b1) // 值类型赋值:b1的修改不会影响a1
fmt.Printf("a2=%v,b2=%v \n", a2, b2) // 引用类型赋值:b2的修改影响了a2
结果:
a1=[2 3 4 5],b1=[6 3 4 5]
a2=[6 3 4 5],b2=[6 3 4 5]
5. 切片常用函数
5.1 创建切片:make
make创建切片,
make([]int, 10, 11)
分配一个底层数组大小为10,返回长度为11、容量为10的切片int类型切片
var slice1 = make([]int, 10, 11)
slice1[2] = 6
slice1[9] = 20
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", slice1, len(slice1), cap(slice1), slice1)
/* 结果:
类型:[]int, 长度:10, 容量:11, 数据:[0 0 6 0 0 0 0 0 0 20]
*/
5.2 扩容切片:append
切片扩容:
append(slice, elem1, elem2)或者append(slice, anotherSlice...)
,slice表示需要扩容的切片,elem*表示添加的元素,anotherSlice表示其他切片
// 切片的扩容不能通过slice[2]下标越界方式,会报错下标越界:index out of range [2] with length 0
var slice2 = make([]int, 0, 0)
// 切片中追加一个元素,并返回最终元素
slice2 = append(slice2, 12, 14, 24)
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", slice2, len(slice2), cap(slice2), slice2)
slice3 := []int{3, 4, 5}
// 切片的合并,slice3...代表slice3中所有的元素列举
var slice4 = append(slice2, slice3...)
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", slice2, len(slice4), cap(slice4), slice4)
/* 结果:
类型:[]int, 长度:3, 容量:3, 数据:[12 14 24]
类型:[]int, 长度:6, 容量:6, 数据:[12 14 24 3 4 5]
*/
5.3 复制切片【深克隆】:copy
深克隆:指的是克隆前后的对象为两个独立的个体,后续修改变更互不影响
浅克隆:指的是克隆前后的对象为同一个引用类型,后续修改A同样影响B的值
copy(dst, src []Type),dst是destination,表示目的变量,src是source,表示对象源,即将src复制给dst
// 切片的扩容不能通过slice[2]下标越界方式,会报错下标越界:index out of range [2] with length 0
var slice2 = make([]int, 0, 0)
// 切片中追加一个元素,并返回最终元素
slice2 = append(slice2, 12, 14, 24)
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", slice2, len(slice2), cap(slice2), slice2)
slice3 := []int{3, 4, 5}
// 切片的合并,slice3...代表slice3中所有的元素列举
var slice4 = append(slice2, slice3...)
fmt.Printf("类型:%T, 长度:%v, 容量:%v, 数据:%v\n", slice2, len(slice4), cap(slice4), slice4)
// 切片的复制深克隆
slice4Copy := make([]int, len(slice4))
copy(slice4Copy, slice4)
slice4Copy[0] = 6666
fmt.Println(slice4, slice4Copy)
/* 结果:
类型:[]int, 长度:3, 容量:3, 数据:[12 14 24]
类型:[]int, 长度:6, 容量:6, 数据:[12 14 24 3 4 5]
[12 14 24 3 4 5] [6666 14 24 3 4 5]
*/
5.4 切片的删除
Go语言中没有内置删除切片方法,可以使用append方法,与切片再切片结合实现
// 切片的删除
s1 := []int{1, 2, 3, 4, 5, 6}
// 删除元素的索引下标
delIndx := 3
// 通过切片append方法,与切片再切片结合实现,即截取[0,3)和[4,5]的切片数据再合并达到删除效果
s1 = append(s1[:delIndx], s1[delIndx+1:]...)
fmt.Println(s1)
/* 结果:
[1 2 3 5 6]
*/
5.5 sort排序
5.5.1 升序排序
使用sort.Ints、sort.Float64s、sort.Strings进行排序
intList := []int{3, 4, 32, 32, 423, 12}
float64List := []float64{3.13, 4.2, 5.3, 1.1}
stringList := []string{"D", "S", "A", "C"}
// 升序排序
sort.Ints(intList)
sort.Float64s(float64List)
sort.Strings(stringList)
fmt.Println(intList)
fmt.Println(float64List)
fmt.Println(stringList)
/* 结果:
[3 4 12 32 32 423]
[1.1 3.13 4.2 5.3]
[A C D S]
*/
5.5.2 降序排序
结合sort.XXXSlice、sort.Reverse和sort.Sort实现
intList := []int{3, 4, 32, 32, 423, 12}
float64List := []float64{3.13, 4.2, 5.3, 1.1}
stringList := []string{"D", "S", "A", "C"}
/*
降序排序
1. sort.IntSlice(intList)表示对intList转成interface,简单理解为将切片转为排序器接口,返回结果类型:data sort.Interface
2. sort.Reverse(data sort.Interface)表示翻转切片,返回结果类型:data sort.Interface
3. sort.Sort(data sort.Interface) 表示将结果排序返回数据
默认当做固定写法:sort.Sort(sort.Reverse(sort.XXXSlice(intList)))
另外也可以自定义排序算法实现降序排序
*/
sort.Sort(sort.Reverse(sort.IntSlice(intList)))
sort.Sort(sort.Reverse(sort.Float64Slice(float64List)))
sort.Sort(sort.Reverse(sort.StringSlice(stringList)))
fmt.Println(intList)
fmt.Println(float64List)
fmt.Println(stringList)
/* 结果:
[423 32 32 12 4 3]
[5.3 4.2 3.13 1.1]
[S D C A]
*/
6. 扩展技巧
6.1 语法提示
对一个变量输入“.”符号时,编译器自动提示语法命令,后缀携带!的表示为功能快捷提示,并生成对应的半成品代码
截图中提示语功能
功能提示语句 | 含义 |
---|---|
append! | 切片追加数据 |
copy! | copy克隆切片 |
for! | fori循环 |
forr! | for range循环 |
ifnotnil! | if not nil 条件判断 |
last! | 等价于a[len(a -1)]获取最后一个元素 |
len! | 等价于len(a)获取长度 |
print! | 打印变量 |
range! | 等价于forr! |
reverse! | 翻转切片 |
sort! | 自定定排序 |
var! | 将a赋值于新的变量 |
6.2 方法参数、含义使用探究
- 鼠标移动至方法名1s以上,显示方法名参数、返回值,含义,描述详情等信息
- 用ctrl按键+鼠标左键,点击方法名,即可进入方法名参数、返回值,含义,描述详情等信息,如果不懂英文,可以通过翻译工具认识逐步了解学习
上一篇:Golang学习历程【第四篇 运算符&流程控制】
下一篇:Golang学习历程【第六篇 复合数据类型:map】 待续中。。。