【Golang第5章:数组与切片】golang如何使用数组、数组的遍历和、使用细节和内存中的布局;golang如何使用切片,切片在内存中的布局,
介绍
这个是在B站上看边看视频边做的笔记,这一章是Go语言的数组与切片
Golang如何使用数组,Golang数据的遍历、切片的遍历,具体请看【文章目录】
配套视频自己去B站里面搜【go语言】,最高的播放量就是
里面的注释我写的可能不太对,欢迎大佬们指出╰(°▽°)╯
文章目录
(五)、数组与切片
一、数组
1.为什么需要数组
看一个问题
一个养鸡场有 6 只鸡,它们的体重分别是 3kg,5kg,1kg,3.4kg,2kg,50kg 。请问这六只鸡的总体重是 多少?平均体重是多少? 请你编一个程序。=》数组
使用传统的方法来解决:
//传统方式
hen1 := 3.0
hen2 := 5.0
hen3 := 1.0
hen4 := 3.4
hen5 := 2.0
hen6 := 50.0
totaWeight := hen1 + hen2 + hen3 + hen4 + hen5 + hen6
avgWeight := fmt.Sprintf("%.2f", totaWeight/6) // .2f表示为浮点数并保留2为小数
fmt.Printf("一共有%vkg,平均重量为%vkg\n", totaWeight, avgWeight)
对上面代码的说明
- 使用传统的方法不利于数据的管理和维护.
- 传统的方法不够灵活,因此我们引出需要学习的新的数据类型=>数组.
使用数组方式:
//数组方式
var hens [6]float64 //1.定义一个数组
// 2.给数组每个元素赋值元素下标是从0开始的 0-5
hens[0] = 3.0 // hens数据第1个元素hens[0]
hens[1] = 5.0 // hens数据第2个元素hens[1]
hens[2] = 1.0
hens[3] = 3.4
hens[4] = 2.0
hens[5] = 50.0 // hens数据第6个元素hens[5]
// 3.遍历数组求出总体重
totaWeight2 := 0.0 //声明变量类型或者 var totaWeight2 float64
for i := 0; i < len(hens); i++ {
totaWeight2 += hens[i]
}
//4.求出平均体重
avgWeight2 := fmt.Sprintf("%.2f", totaWeight2/float64(len(hens))) // .2f表示为浮点数并保留2为小数
fmt.Printf("一共有%vkg,=平均重量为%vkg\n", totaWeight2, avgWeight2)
对上面代码的总结
- 使用数组来解决问题,程序的可维护性增加.
- 而且方法代码更加清晰,也容易扩展。
介绍:
数组可以存放多个同一类型数据。数组也是一种数据类型,在Go中,数组是值类型。
2.数组定义和内存布局
1)数组的定义
var 数组名 [数组大小]数据类型
var a [5]int
赋初值 a[0] = 1 a[1] = 30 …
数组在内存布局(重要)
var intArr [3]int //存放3个值的数组,当我们定义完数组后,其实数组的各个元素有默认值 0
fmt.Println(intArr)
fmt.Printf("intArr数组的地址=%p\nintArr[0]数组第1个元素的地址%p\nintArr[1]数组第2个元素的地址%p\nintArr[2]数组第3个元素的地址%p\n",
&intArr, &intArr[0], &intArr[1], &intArr[2]) //数组的地址和第一个元素地址相同,数组元素的地址是连续的,int有8个字节
输出:
[0 0 0]
intArr数组的地址=0xc0000101c8
intArr[0]数组第1个元素的地址0xc0000101c8
intArr[1]数组第2个元素的地址0xc0000101d0
intArr[2]数组第3个元素的地址0xc0000101d8
var intArr [3]int32 //存放3个值的数组,当我们定义完数组后,其实数组的各个元素有默认值 0
fmt.Println(intArr)
intArr[0] = 10
intArr[1] = 20
intArr[2] = 30
fmt.Println(intArr)
fmt.Printf("intArr数组的地址=%p\nintArr[0]数组第1个元素的地址%p\nintArr[1]数组第2个元素的地址%p\nintArr[3]数组第3个元素的地址%p\n",
&intArr, &intArr[0], &intArr[1], &intArr[2]) //数组的地址和第一个元素地址相同,数组元素的地址是连续的,int32有4个字节
输出:
[0 0 0]
[10 20 30]
intArr数组的地址=0xc0000120a0
intArr[0]数组第1个元素的地址0xc0000120a0
intArr[1]数组第2个元素的地址0xc0000120a4
intArr[3]数组第3个元素的地址0xc0000120a8
总结:
- 数组的地址可以通过数组名来获取 &intArr
- 数组的第一个元素的地址,就是数组的首地址
- 数组的各个元素的地址间隔是依据数组的类型决定,比如 int64 -> 8 int32->4…
2)数组的使用
从终端循环输入5个成绩,保存到float64数组,并输出
// 从终端循环输入5个成绩,保存到float64数组,并输出
var score [5]float64
for i := 0; i < len(score); i++ {
fmt.Printf("请输入第%d个元素的值\n", i+1)
fmt.Scanln(&score[i]) //从终端获取值并传入到数组
}
for i := 0; i < len(score); i++ {
fmt.Printf("score[%d]=%v\n", i, score[i])
}
四种初始化数组的方式
//四种初始化数组的方式,可以使用类型推导num5 := [...]int{}
var num1 [3]int = [3]int{1, 3, 4}
fmt.Println("num1=", num1)
var num2 = [3]int{1, 32, 1}
fmt.Println("num2=", num2)
var num3 = [...]int{1, 3, 1} //这里的[...]是规定写法
fmt.Println("num3=", num3)
var num4 = [...]int{1: 800, 0: 900, 2: 100} //指定下标元素位置
fmt.Println("num4=", num4)
3)数组的遍历
方式 1-常规遍历:
前面已经讲过了,不再赘述。
方式 2-for-range 结构遍历
这是 Go 语言一种独有的结构,可以用来遍历访问数组的元素。
基本语法
for index, value := range array01 {
...
}
数组:[1:300 , 2:100 , 0:900 , 3:400]
顺序:[900,300,100,400]
第一个返回值index
是数组的下标,例如:下标[0]
,下标[1]
第二个value
是在该下标位置的值,例如:数值[900]
,数值,[300]
他们都是仅在for
循环内部可见的局部变量
遍历数组元素的时候,如果不想使用下标index
,可以直接把下标index
标为下划线_
进行忽略
index
和value
的名称不是固定的。即程序员可以自行指定,一般命名为index
和value
案例:
//for-range 结构遍历
name := [3]string{"宋江", "吴用", "卢俊义"}
fmt.Println(name)
for i, v := range name {
fmt.Printf("下标i=%v,数值v=%v\n", i, v) //for-range方式
fmt.Printf("name[%d]=%v\n", i, name[i]) //传统方式
}
输出:
[宋江 吴用 卢俊义]
下标i=0,数值v=宋江
name[0]=宋江
下标i=1,数值v=吴用
name[1]=吴用
下标i=2,数值v=卢俊义
name[2]=卢俊义
4)数组使用的注意事项和细节
- 1.数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化
func main() {
//数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化
var arr01 [3]int //数组声明了类型后,只能存入声明的类型
arr01[0] = 1
arr01[1] = 10
// arr01[2] = 1.1//数组声明了类型后,只能存入声明的类型
arr01[2] = 31
//arr01[3] = 20 //超过设定的数组长度
}
- 2.
var arr []int
这时arr
就是一个slice
切片,切片后面专门讲解
- 3.数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
- 4.数组创建后,如果没有赋值,有默认值(零值)
数值类型数组:默认值为
0
字符串数组: 默认值为
""
bool 数组: 默认值为
false
var arr01 [3]int
var arr02 [3]float64
var arr03 [3]string
var arr04 [3]bool
fmt.Println(arr01, arr02, arr03, arr04)
输出:
[0 0 0] [0 0 0] [ ] [false false false]
- 5.使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值(默认零值) 3 使用数组
- 6.数组的下标是从 0 开始的
- 7.数组下标必须在指定范围内使用,否则报 panic:数组越界,比如 var arr [5]int 则有效下标为 0-4
- 8.Go 的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影
func test1(arr [3]int) {
arr[0] = 88 //3.把88赋值给第一个下标[0],当前数组值为[88 22 33]
fmt.Println(arr) //4.输出(test1)栈的arr,[88 22 33]
}
func main() {
//Go 的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影
arr := [3]int{11, 22, 33} //1.赋值数组给arr,在一个(mian)栈里面,当前数组值为[11 22 33]
test1(arr) //2.把arr进行值拷贝,并重新生成一个(test1)栈,当前数组值为[11 22 33]
fmt.Println(arr) //5.输出(main)栈的arr,[11 22 33]
}
- 9.如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
func test2(arr *[3]int) { //4.使用*获取arr原值
(*arr)[0] = 88 //5.修改main下arr的第一个下标[0],当前数组值为[88 22 33]
}
func main() {
arr := [3]int{11, 22, 33} //1.赋值数组给arr,在一个(mian)栈里面,当前数组值为[11 22 33]
fmt.Printf("arr地址=%p\n", &arr) //2.打印arr地址
test2(&arr) //3.生成一个(test2)栈,并把arr地址传入(指针)到test2
fmt.Println(arr) //6.输出被指针修改过后的数组[88 22 33]
}
- 10. 长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度,看下面案例
长度是数组的一部分,由于数组长度不一致,会编译错误
5)数组的应用案例
- 1.创建一个 byte 类型的 26 个元素的数组,分别 放置’A’-'Z‘。使用 for 循环访问所有元素并打印 出来。提示:字符数据运算 ‘A’+1 -> 'B
func test1() {
//1.创建一个 byte 类型的 26 个元素的数组,分别 放置'A'-'Z‘。
//使用 for 循环访问所有元素并打印 出来。提示:字符数据运算 'A'+1 -> 'B
//思路
//1.声明一个数组
//2.使用for循环,利用字符可以进行运算的特点来赋值'A'+1 -> 'B
//3.使用for打印即可
var myChars [26]byte
fmt.Println('A') //ASCII表十进制的A是65
for i := 0; i < 26; i++ {
myChars[i] = 'A' + byte(i) //类型不匹配,A是byte类型,需要把i的int类型改为byte类型
}
for i := 0; i < 26; i++ {
fmt.Printf("%c ", myChars[i]) //输出该值对应的ASCII码值
}
}
- 2.请求出一个数组的最大值,并得到对应的下标
func test2() {
//请求出一个数组的最大值,并得到对应的下标
//思路
//1.声明一个数组var intArr[5] = [...]int {1, -2, 9, 43, 11, 9}
//2. 假定第一个元素就是最大值,下标就e
//3.然后从第一个元素开始循环比较,如果发现有更大,则交换
var intArr = [...]int{1, -2, 9, 43, 11, 9} //1.声明一个数组
maxVal := intArr[0] //2.获取数组
maxValIndex := 0 //3.声明变量
for i := 0; i < len(intArr); i++ {
if maxVal < intArr[i] { //4.判断大小如果发现有更大,则执行if内变量
maxVal = intArr[i]
maxValIndex = i
}
}
fmt.Printf("数组最大值为:%v,其下标为:%v", maxVal, maxValIndex)
}
- 3.请求出一个数组的和和平均值。for-range遍历
func test3() {
//请求出一个数组的和和平均值。for-range遍历
//思路
//1.就是声明一个数组var intArr = [...]int{1, -1, 9, 90, 11}
//2.求出和sum
//3.求出平均值
var intArr = [...]int{1, -1, 9, 90, 12}
sum := 0 //声明sum
for _, val := range intArr { //使用range对数组进行遍历
sum += val //加等所有数组内的值
}
fmt.Printf("sum的和为%v,平均值为%v\n", sum, float64(sum)/float64(len(intArr))) //使用len统计字符串的长度
}
- 4.要求:随机生成五个数,并将其反转打印 , 复杂应用
func test4() {
//要求:随机生成五个数,并将其反转打印
//思路
//1. 随机生成五个数,rand.Intn() 函数(rand.Intn是伪随机数)
//2. 当我们得到随机数后,就放到一个数组int数组
//3. 反转打印,交换的次数是len/2,倒数第1个和第1个元素交换,倒数第2个和第2个元素交换
var intArr [5]int
rand.Seed(time.Now().UnixNano()) //1.为了每次生成的随机数不-样,我们需要 给个seed值
for i := 0; i < len(intArr); i++ {
intArr[i] = rand.Intn(100) //2.为了每次生成的随机数不-样,在前面给了个seed值
}
fmt.Println("交换前=", intArr)
//反转打印,交换的次数是len/2,倒数第1个和第1个元素交换,倒数第2个和第2个元素交换
temp := 0 //做一个临时变量
for i := 0; i < len(intArr)/2; i++ {
temp = intArr[len(intArr)-1-i] //3.把inArr第5个下标[4]的值赋值给临时变量
intArr[len(intArr)-1-i] = intArr[i] //4.把inArr第1个下标[0]的值赋值给inArr第5个下标[4]的值
intArr[i] = temp //5.把temp临时变量赋值给inArr第1个下标[0]的值
}
fmt.Println("交换后=", intArr)
}
二、切片Slice
1.为什么需要切片
先看一个需求: 我们需要一个数组用于保存学生的成绩,但是学生的个数是不确定的,请问怎么 办?解决方案:使用切片。
2.切片的基本介绍
-
切片的英文是 slice
-
切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
-
切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度 len(slice)都一样。
-
切片的长度是可以变化的,因此切片是一个可以动态变化数组
-
切片定义的基本语法:
var 切片名 [] 类型
比如:var a [] int
3.切片的基本使用
演示:
func main() {
//演示切片的基本使用
var intArr = [...]int{23, 33, 66} //1.声明一个数组
//定义一个切片
/*
slice就是切片名
intArr[1:3]表示slice引用intArr这个数组其下标为[1]的值到这个数组下标为[3]的值,不包括下标[3]
*/
slice := intArr[1:2]
fmt.Println("原数组是=", intArr)
fmt.Println("slice切片后是=", slice)
fmt.Println("切片后slice数组数量是=", len(slice))
fmt.Println("切片后slice数组的容量是=", cap(slice)) //cap返回v的容量,切片会在原有的容量上预先-1,例如原数组有3个元素,其slice容量是2
}
输出:
PS D:\code\Go\src\demo\22demo\main> go run .\main.go
原数组是= [23 33 66]
slice切片后是= [33]
切片后slice数组数量是= 1
切片后slice数组的容量是= 2
4.切片在内存中形式(重要)
-
基本的介绍:
为了让大家更加深入的理解切片,我们画图分析一下切片在内存中是如何布局的,这个是一个非 常重要的知识点:(以前面的案例来分析)
-
画出前面的切片内存布局
-
对上面的分析图总结
1.slice 的确是一个引用类型
2.slice 从底层来说,其实就是一个数据结构(struct 结构体)
type slice struct { ptr *[2]int len int cap int }
5.切片的使用
方式 1
第一种方式: 定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这样的。
方式 2
第二种方式: 通过 make 来创建切片.
基本语法:var 切片名 []type = make([]type, len, [cap])
参数说明:
- type: 就是数据类型
- len : 大小 cap :指定切片容量,可选, 如果你分配了 cap,则要 求 cap>=len
//演示切片的使用
var slicetest []float64 //对于切片,必须make使用,不然没有元素值
fmt.Println(slicetest)
//对于切片,必须make使用
var slice []float64 = make([]float64, 5, 10) //声明切片里面有5个元素,容量为10
fmt.Println(slice)
slice[1] = 10 //给切片数组进行赋值
slice[3] = 20
fmt.Println(slice)
fmt.Printf("slice的大小=%v\nslice的容量=%v\n", len(slice), cap(slice))
对上面代码的小结:
- 通过 make 方式创建切片可以指定切片的大小和容量
- 如果没有给切片的各个元素赋值,那么就会使用默认值
[int , float=> 0 ; string =>"" ; bool => false]
- 通过 make 方式创建的切片对应的数组是由 make 底层维护,对外不可见,即只能通过 slice 去 访问各个元素.
方式 3
第 3 种方式: 定义一个切片,直接就指定具体数组,使用原理类似 make 的方式
案例演示:
//定义一个切片,直接就指定具体数组,使用原理类似 make 的方式
var strSlice []string = []string{"tom", "jack", "mary"}
fmt.Println(strSlice)
fmt.Println(len(strSlice))
fmt.Println(cap(strSlice))
方式 1 和 2 的区别
方式1是:直接引用数组,这个数组是事先存在的,程序员是可见的。
方式2是:通过make来创建切片,make也会创建一一个 数组,是由切片在底层进行维护,程序员是看不见的。
make创建切片的示意图:
6.切片的遍历
切片的遍历和数组一样,也有两种方式
-
使用常规for循环遍历切片
//使用常规for循环遍历切片 var arr = [...]int{110, 20, 30, 43, 50} slice := arr[1:4] //20 30 43 for i := 0; i < len(slice); i++ { fmt.Printf("slice[%v]=%v\n", i, slice[i]) }
-
for-range 结构遍历切片
//for-range 结构遍历切片 for i, v := range slice { fmt.Printf("下标i=%v 原值=%v\n", i, v) }
7.切片的注意事项
1)切片初始化时 var slice = arr[startIndex:endIndex]
。
说明:从 arr
数组下标为 startIndex
,取到 下标为 endIndex
的元素(不含 arr[endIndex]
)。
2)切片初始化时,仍然不能越界。范围在 [0-len(arr)]
之间,但是可以动态增长。
var slice = arr[0:end]
可以简写 var slice = arr[:end]
var slice = arr[start:len(arr)]
可以简写: var slice = arr[start:]
var slice = arr[0:len(arr)]
可以简写: var slice = arr[:]
3)cap
是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
4)切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者 make
一 个空间供切片来使用。
5)切片可以继续切片[案例演示]。
//使用常规for循环遍历切片
var arr = [...]int{110, 20, 30, 43, 50, 60}
slice := arr[1:4] //20 30 43
for i := 0; i < len(slice); i++ {
fmt.Printf("slice[%v]=%v\n", i, slice[i])
}
//for-range 结构遍历切片
for i, v := range slice {
fmt.Printf("下标i=%v 原值=%v\n", i, v)
}
//可以进行二次切片[20 30 43]
slice2 := slice[1:2]
fmt.Println(slice2)
slice2[0] = 100 //因为arr,slIce和slice2指向的数据空间是同一个,因此slice2[0]=100,其它的都变化
fmt.Println("slice2=", slice2) //[100]
fmt.Println("slice=", slice) //[20 100 43]
fmt.Println("arr=", arr) //[110 20 100 43 50 60]
6)用 append 内置函数,可以对切片进行动态追加
//用 append 内置函数,可以对切片进行动态追加
var slice []int = []int{100, 200, 300}
fmt.Println("slice", slice) //slice [100 200 300]
//通过append直接slice追加具体元素
slice = append(slice, 400, 500, 600) //生产一个新的空间,先把原先的slice复制到新空间[100 200 300 0 0 0],在把追加的append复制到新空间[100 200 300 400 500 600],再用slice引用到此空间
fmt.Println("slice", slice) //slice [100 200 300 400 500 600]
//通过append将切片slice追加给slice
slice = append(slice, slice...) //...是规定写法
fmt.Println("slice", slice) //slice [100 200 300 400 500 600 100 200 300 400 500 600]
对上面代码的小结:
切片 append 操作的底层原理分析:
切片 append 操作的本质就是对数组扩容
go 底层会创建一下新的数组 newArr(安装扩容后大小)
将 slice 原来包含的元素拷贝到新的数组 newArr
slice 重新引用到 newArr
注意 newArr 是在底层来维护的,程序员不可见
7)切片的拷贝操作
切片使用 copy 内置函数完成拷贝,举例说明
//切片的拷贝操作,这是2个独立的空间,不会相互影响
var a []int = []int{1, 2, 3, 4, 5} //这是2个独立的空间
var slice = make([]int, 10)
copy(slice, a) //把切片a里面的元素拷贝到slice切片里面
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(slice) //[1 2 3 4 5 0 0 0 0 0]
对上面代码的说明:
(1) copy(para1, para2)
参数的数据类型是切片
(2) 按照上面的代码来看, a和 slice 的数据空间是独立,相互不影响,也就是说 a[0]= 999
, slice[0]
仍然是
8)关于拷贝的注意事项
说明: 上面的代码没有问题,可以运行, 最后输出的是 [1
9)切片是引用类型,所以在传递时,遵守引用传递机制。看两段代码,并分析底层原理
8.string 和 slice
1.string 底层是一个 byte 数组,因此 string 也可以进行切片处理 案例演示:
func main() {
//1) string 底层是一个 byte 数组,因此 string 也可以进行切片
str := "hello@nihao你好" // "你好"占有6个字符
slice := str[6:]
fmt.Println(slice) //nihao你好
}
2.string 和切片在内存的形式,以 “abcd”
3.string 是不可变的,也就说不能通过 str[0] = 'z'
方式来修改字符串
4.如果需要修改字符串,可以先将 string
-> []byte
/ 或者 []rune
-> 修改 -> 重写转成 string
func main() {
//1) string 底层是一个 byte 数组,因此 string 也可以进行切片
str := "hello@nihao你好" // 中文一个字占有3个字节 ,不建议有中文,会出现乱码
slice := str[6:]
fmt.Println(slice) //nihao你好
//把"hello@nihao你好" =》改成 "zello@nihao你好"
arr1 := []byte(str) //把str改为byte切片 , 改中使用[]rune切片
fmt.Println(arr1)
arr1[0] = 'z' //把 h 改为 z
str = string(arr1) // 把arr1byte切片转换为string类型
fmt.Println(str)
//细节,我们转成[]byte后,可以处理英文和数字,但是不能处理中文
//原因是[]byte 字节来处理,而一个汉字,是3个字节,因此就会出现乱码
//解决方法是将string 转成[]rune 即可
arr2 := []rune(str)
arr2[0] = '北'
str = string(arr2)
fmt.Println(str)
}
9.切片的练习题
说明:编写一个函数fbn(n int)
,要求完成
- 可以接收一个
n int
- 能够将斐波那契的数列放到切片中
- 提示, 斐波那契的数列形式:
arr[0] = 1; arr[1] = 1; arr[2]=2; arr[3] = 3; arr[4]=5; arr[5]=8
package main
import "fmt"
/*
说明:编写一个函数`fbn(n int)`,要求完成
1) 可以接收一个`n int`
2) 能够将斐波那契的数列放到切片中
3) 提示, 斐波那契的数列形式:
arr[0] = 1; arr[1] = 1; arr[2]=2; arr[3] = 3; arr[4]=5; arr[5]=8
思路:
1.声明一个函数fbn(n int) ([]uint64)
2.编程fbn(n int) 进行for循环来存放斐波那契的数列 0 =》1 1 =》 1
*/
func fbn(n int) []uint64 { //接收main传入的参数10
fbnSlice := make([]uint64, n) //声明一个切片,切片大小为n
fbnSlice[0] = 1 //第一个数和第二个数的斐波那契都为1
fbnSlice[1] = 1
for i := 2; i < n; i++ {
fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2] //fbnSlice[2] = fbnSlice[1] + fbnSlice[0]
}
return fbnSlice
}
func main() {
fnbSlice := fbn(10) //传入参数10,并把接收到的值赋值给fbnSlice
fmt.Println(fnbSlice)
}
章节目录
【Golang第1~3章:基础】如何安装golang、第一个GO程序、golang的基础
【Golang第4章:函数】Golang包的引用,return语句、指针、匿名函数、闭包、go函数参数传递方式,golang获取当前时间
【Golang第5章:数组与切片】golang如何使用数组、数组的遍历和、使用细节和内存中的布局;golang如何使用切片,切片在内存中的布局
【Golang第6章:排序和查找】golang怎么排序,golang的顺序查找和二分查找,go语言中顺序查找二分查找介绍和案例
【Golang第7章:map】go语言中map的基本介绍,golang中map的使用案例,go语言中map的增删改查操作,go语言对map的值进行排序
【Golang第8章:面向对象编程】Go语言的结构体是什么,怎么声明;Golang方法的调用和声明;go语言面向对象实例,go语言工厂模式;golang面向对象的三大特性:继承、封装、多态
【Golang第9章:项目练习】go项目练习家庭收支记账软件项目、go项目练习客户管理系统项目
【Golang第10章:文件操作】GO语言的文件管理,go语言读文件和写文件、GO语言拷贝文件、GO语言判断文件是否存在、GO语言Json文件格式和解析
【Golang第12章:goroutine协程与channel管道】GO语言goroutine协程和channel管道的基本介绍、goroutine协