欢迎来到Golang的世界!在当今快节奏的软件开发领域,选择一种高效、简洁的编程语言至关重要。而在这方面,Golang(又称Go)无疑是一个备受瞩目的选择。在本文中,带领您探索Golang的世界,一步步地了解这门语言的基础知识和实用技巧。
目录
初识数组
二维数组
slice切片
map映射
初识数组
在Go语言中,数组是一种固定长度的、包含相同类型元素的序列。数组的长度是类型的一部分,因此[5]int和[10]int是两种不同的类型。数组在Go中是值类型,这意味着当你传递一个数组到函数或将其赋值给另一个变量时,实际上是在复制整个数组的内容。
在Go语言中数组的内存分配是静态的,并且是在编译时确定的。由于数组的长度是类型的一部分,所以不同长度的数组在内存中的表示和占用是不同的。以下是关于Go语言中数组内存分析的代码示例:
package main import "fmt" func main() { // 声明数组 var arr [3]int16 // 获取数组长度 fmt.Println(len(arr)) // 3 // 打印数组 fmt.Println(arr) // [0 0 0] // 证明arr中存储的是地址值 fmt.Printf("arr的地址为:%p \n", &arr) // arr的地址为:0xc0000100b0 // 第一个空间地址 fmt.Printf("arr[0]的地址为:%p \n", &arr[0]) // arr[0]的地址为:0xc0000100b0 // 第二个空间地址 fmt.Printf("arr[1]的地址为:%p \n", &arr[1]) // arr[1]的地址为:0xc0000100b2 // 第三个空间地址 fmt.Printf("arr[2]的地址为:%p \n", &arr[2]) // arr[2]的地址为:0xc0000100b4 }
因为int占两个字节,所以地址值之间间隔为2,如下是图例解释:
接下来我们通过用户终端输入的方式,将数据存入数组当中,然后遍历数组,将数据打印出来:
package main import "fmt" func main() { // 定义数组 var scores [5]int // 将成绩存入数组 for i := 0; i < len(scores); i++ { fmt.Printf("请录入第%d个学生的成绩", i+1) fmt.Scanln(&scores[i]) } // 展示一下录入学生的成绩:(对数组进行遍历) for i := 0; i < len(scores); i++ { fmt.Printf("第%d个学生的成绩为:%d \n", i, scores[i]) } // 求和 sum := 0 for i := 0; i < len(scores); i++ { sum += scores[i] } // 平均数 avg := sum / len(scores) // 输出 fmt.Printf("成绩的总和为:%v, 成绩的平均数为:%v", sum, avg) }
最终呈现的效果如下所示:
当然这里也可以采用for range的方式,for range结构是go语言中特有的一种迭代结构,在许多情况下都非常有用,for range可以遍历数组、切片、字符串、map及通道,示例代码如下,最终也能得到上面图片的结果:
func main() { // 定义数组 var scores [5]int // 将成绩存入数组 for i := 0; i < len(scores); i++ { fmt.Printf("请录入第%d个学生的成绩", i+1) fmt.Scanln(&scores[i]) } // 展示一下录入学生的成绩:(对数组进行遍历) for range方式 for key, value := range scores { fmt.Printf("第%d个学生的成绩为:%d \n", key+1, value) } }
数组可以有多种书写方式,如下可以看到我们数组的书写方式:
package main import "fmt" func main() { // 数组的几种书写方式 // var scores [5]int = [5]int{94, 98, 89, 80, 90} // 基本写法 // var scores = [5]int{94, 98, 89, 80, 90} // 省略类型和长度 //var scores = [...]int{2: 66, 0: 33, 1: 99, 3: 88} // 确定数组下标对应的数值 scores := []int{94, 98, 89, 80, 90} // 使用切片(slice)语法(这在Go中更为常见) sum := 0 for i := 0; i < len(scores); i++ { // 求得成绩的总值 sum += scores[i] } // 平均数 avg := sum / len(scores) // 输出 fmt.Printf("成绩的总和:%v, 成绩的平均数为:%v\n", sum, avg) }
在go语言中,关于数组还有以下注意事项,这里做一个简单的概述:
长度属于类型的一部分:
func main() { // 定义一个数组 var arr1 = [3]int{1, 2, 3} fmt.Printf("数组的类型为:%T", arr1) // [3]int var arr2 = [6]int{3, 4, 5, 6, 7, 8} fmt.Printf("数组的类型为:%T", arr2) // [6]int }
数组属于值类型,在默认情况下是值传递,因此会进行值拷贝,如下图所示我对数组的值进行修改是修改了arr的值,并不是arr3的:
如想在其它函数中去修改原来的数组,可以使用引用传递(指针方式):
package main import "fmt" func test(arr *[3]int) { (*arr)[0] = 7 } func main() { var arr3 = [3]int{3, 6, 9} test(&arr3) // 传入arr3数组的地址 fmt.Println(arr3) // [7 6 9] }
二维数组
在Go语言中,二维数组(也称为矩阵)是一个数组,其中每个元素都是另一个数组。这意味着你可以有两个维度来索引数据:第一个维度表示行,第二个维度表示列。示例代码如下:
func main() { var arr1 [2][3]int = [2][3]int{{1, 2, 3}, {4, 5, 6}} fmt.Println(arr1) // [[1 2 3] [4 5 6]] }
在下面的例子中,array2D 是一个5行3列的二维整型数组,这里可以通过array2D[row][column]的方式来访问或修改数组中的元素:
func main() { // 声明一个5行3列的二维整型数组 var array2D [5][3]int // 初始化时直接赋值 array2D = [5][3]int{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}, } // 访问二维数组中的元素 fmt.Println(array2D[2][1]) // 输出: 8 }
如果想对二维数组进行遍历的话,需要用到双层for循环,这里进行一个简单的示例:
package main import "fmt" func main() { // 定义二维数组 var arr = [3][3]int{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}} fmt.Println(arr) fmt.Println("---------------") // 方式1:普通for循环 for i := 0; i < len(arr); i++ { for j := 0; j < len(arr[i]); j++ { fmt.Print(arr[i][j], "\t") } fmt.Println() } // 方式2:for range循环 for key, value := range arr { for k, v := range value { fmt.Printf("arr[%v][%v] = %v \t", key, k, v) } fmt.Println() } }
最终达到的效果如下所示:
slice切片
在Go语言中,切片(slice)是对数组的一个连续片段的引用,它本身并不包含数组的数据,而是包含了对底层数组的引用、长度以及容量。切片提供了一种灵活、方便且高效的方式来操作数组的一个子集,是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷,如下代码给出切片的示例:
package main import "fmt" func main() { // 定义数组 var intarr [6]int = [6]int{3, 6, 7, 1, 2, 3} // 定义切片 slice [1:3]表示切出的一段片段,索引从1开始,到3结束 slice := intarr[1:3] // 输出数组 fmt.Println("intarr: ", intarr) // intarr: [3 6 7 1 2 3] // 输出切片 fmt.Println("slice: ", slice) // slice: [6 7] // 切片元素个数 fmt.Println("slice的元素个数: ", len(slice)) // slice的元素个数: 2 // 获取切片的容量,容量可以动态变化 fmt.Println("slice的容量: ", cap(slice)) // slice的容量: 5 }
切片由3个字段的数据结构组成:指向底层数组的指针、切片的长度、切片的容量
切片的创建:在Go语言中,make 是一个内置函数,用于动态地分配并初始化对象,这些对象包括切片(slices)、映射(maps)和通道(channels)。特别是当我们谈论切片时,make 是创建它们的主要方式,使用 make 创建切片的基本语法如下所示:
slice := make([]T, length, capacity)
这里通过如下代码进行简单示例,在下面的示例中,我们创建了一个整数类型的切片 slice,其长度为 3,容量为 5。这意味着切片当前可以存储 3 个整数,但底层数组的大小为 5,因此将来可以在不重新分配内存的情况下向切片添加最多 2 个额外的整数。
func main() { // 定义切片:make函数的三个参数:1、切片类型 2、切片长度 3、切片容量 slice := make([]int, 3, 5) fmt.Println(slice) // [0 0 0] fmt.Println("切片的长度", len(slice)) // 切片的长度 3 fmt.Println("切片的容量", cap(slice)) // 切片的容量 5 }
make底层创建一个数组,对外不可见,所以不可以直接操作这个数组,要通过slice去间接的访问各个元素,不可以直接对数组进行维护/操作。
切片的遍历:对切片的遍历可以采用如下的方式进行:
func main() { // 定义切片:make函数的三个参数:1、切片类型 2、切片长度 3、切片容量 slice := make([]int, 4, 20) slice[0] = 66 slice[1] = 77 slice[2] = 88 slice[3] = 99 // 方式1:普通for循环,遍历切片 for i := 0; i < len(slice); i++ { fmt.Printf("slice[%v] = %v \t", i, slice[i]) } fmt.Println("\n------------------------") // 方式2:for range循环,遍历切片 for index, value := range slice { fmt.Printf("slice[%v] = %v \t", index, value) } }
最终呈现的效果如下所示:
切片的注意事项:
1)切片定义后不可以直接使用,需要让其引用到一个数组,或者make一个空间供切片来使用
2)切片使用不能越界
3)切片的简写方式
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[:]
4)切片可以继续切片
5)切片可以动态增长
func main() { // 定义数组 var intarr [6]int = [6]int{1, 4, 7, 3, 6, 9} // 定义切片 var slice []int = intarr[1:4] // 4 7 3 fmt.Println(len(slice)) // 3 slice2 := append(slice, 88, 50) fmt.Println(slice2) // [4 7 3 88 50] fmt.Println(slice) // [4 7 3] /* 底层原理 1.底层追加元素的时候对底层数组扩容,老数组扩容为新数组 2.创建一个新数组,将老数组中的 4,7,3 拷贝到新数组中,在新数组中追加 88,50 3.slice2底层数组的指向 指向的是新数组 4.往往我们在使用追加的时候其实想要做的效果是给slice追加元素,而不是给底层数组追加元素 */ slice = append(slice, 10) fmt.Println(slice) // [4 7 3 10] // 底层的新数组不能直接维护,因为slice2底层数组的地址已经变了,所以我们需要重新创建一个新的切片,通过切片间接操作 slice3 := []int{99, 44} slice = append(slice, slice3...) fmt.Println(slice) // [4 7 3 10 99 44] }6)切片的拷贝
func main() { // 定义数组 var a []int = []int{1, 4, 7, 3, 6, 9} // 再定义一个切片 var b []int = make([]int, 10) // 拷贝 copy(b, a) // 将a中的值拷贝到b中 fmt.Println(b) // [1 4 7 3 6 9 0 0 0 0] }
map映射
在Go语言中,map 是一种内置的数据结构,用于存储键值对(key-value pairs)的集合。map 提供了根据键来存储、检索和删除值的能力,使得在Go中处理关联数据变得非常方便,接下来通过如下代码示例进行演示:
func main() { // 定义map变量 /* map: 这表示我们正在声明一个 map 类型的变量 [int]string: 这部分定义了 map 的键和值的类型 具体来说,int 是键的类型,而 string 是值的类型 这意味着你可以将整数用作键,并将字符串作为与这些键相关联的值存储在 map 中 */ var a map[int]string // 只声明map内存是没有分配空间的,需要使用make函数初始化分配空间 a = make(map[int]string, 10) // map可以存放10个键值对,10可以不传,默认会分配一个起始大小 // 初始化map a[3] = "one" a[1] = "two" a[2] = "three" // 输出集合 fmt.Println(a) // map[1:one 2:two 3:three] }
注意:map集合在使用前一定要make;map的key是按照从小到大排序;key值不能重复,如果重复后一个value会对前一个进行覆盖,value是可以重复的;make函数的第二个参数size可以省略,默认就分配一个内存
map的创建方式可以通过如下的操作进行:
func main() { // map的创建方式 // 方式1 a := make(map[int]string) a[2020] = "张三" a[2019] = "李四" fmt.Println(a) // map[2019:李四 2020:张三] // 方式2 b := map[int]string{ 2019: "张三", 2020: "李四", } b[2018] = "王五" fmt.Println(b) // map[2018:王五 2019:张三 2020:李四] }
当然我们也可以通过map对数值进行增删改查的操作,具体如下:
增加和更新操作:map["key"]= value 一》如果key还没有,就是增加,如果key存在就是修改。
func main() { a := make(map[int]string) // 增加操作 a[2020] = "张三" a[2019] = "李四" // 修改操作 a[2020] = "张三!!!" fmt.Println(a) // map[2019:李四 2020:张三!!!] }
删除操作:delete(map,“key"),delete是一个内置函数,如果key存在,就删除该key-value,如果k的y不存在,不操作,但是也不会报错。
func main() { a := make(map[int]string) // 增加操作 a[2020] = "张三" a[2019] = "李四" // 删除操作 delete(a, 2019) fmt.Println(a) // map[2020:张三] }
清空操作:如果我们要删除map的所有key,没有一个专门的方法一次删除,可以遍历一下key,逐个删除;或者map=make(),make一个新的,让原来的成为垃圾,被gc回收。
查找操作:value ,bool = map[key];value为返回的value,bool为是否返回,要么true要么false。
func main() { a := make(map[int]string) // 增加操作 a[2020] = "张三" a[2019] = "李四" // 查找操作 value, flag := a[2019] if flag { fmt.Println(value) // 李四 } else { fmt.Println("查找不到") } fmt.Println(a) // map[2020:张三] }
当然map还有一些其他的操作,这里进行一个简单的演示:
package main import "fmt" func main() { a := make(map[int]string) a[2020] = "张三" a[2019] = "李四" a[2018] = "王五" // 获取长度 fmt.Println(len(a)) // 遍历 for k, v := range a { fmt.Printf("key为:%v value为 %v \n", k, v) } // 上操作 b := make(map[string]map[int]string) // 嵌套map // 赋值 b["班级1"] = make(map[int]string) b["班级1"][2020] = "张三" b["班级1"][2019] = "李四" b["班级1"][2018] = "王五" b["班级2"] = make(map[int]string) b["班级2"][2019] = "张三1" b["班级2"][2018] = "李四1" b["班级2"][2017] = "王五1" // 遍历 for k, v := range b { fmt.Println(k) for k1, v1 := range v { fmt.Printf("key为:%v value为 %v \n", k1, v1) } } }
最终呈现的效果如下:
还没有评论,来说两句吧...