一、数组
1.什么是数组
在Go语言中,数组是一种固定大小的数据结构,用于存储相同类型的元素。数组的大小在声明时就确定,并且不能更改。Go语言的数组声明方式如下:
var 数组名 [大小]元素类型
例如,以下是一个包含5个整数的数组的声明:
var myArray [5]int
在这个例子中,myArray
是一个包含5个整数的数组,每个元素的类型都是 int
。数组的索引从0开始,所以可以通过 myArray[0]
、myArray[1]
、myArray[2]
等方式访问数组的元素。
数组的初始化可以使用以下方式:
myArray := [5]int{1, 2, 3, 4, 5}
或者,可以省略数组大小,让编译器根据初始化值的数量推断数组大小:
myArray := [...]int{1, 2, 3, 4, 5}
需要注意的是,由于数组是固定大小的,一旦声明后,其大小不能更改。如果你需要一个可以动态调整大小的集合,可以使用切片(slice)代替数组。
2.初始化数组
package main
import "fmt"
// 数组的赋值初始化
func main() {
// 1.在定义数组的时候就直接初始化
var arr1 = [5]int{1, 2, 3, 4, 5}
fmt.Println(arr1)
// 2.快速初始化 :=
arr2 := [5]int{1, 2, 3, 4, 5}
fmt.Println(arr2)
// 3.数据如果来自用户,我不知道用户给我多少个数据
// ... 代表数组的长度
// Go的编译器会自动根据数组的长度来给 ... 赋值,自动推导长度
// 注意点:这里的数组不是无限长的,也是固定的大小,大小取决于数组元素个数。
var arr3 = [...]int{1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 8}
fmt.Println(len(arr3))
fmt.Println(arr3)
// 4.数组默认值,我只想给其中的某几个index位置赋值。
// {index:值}
var arr4 [10]int
arr4 = [10]int{1: 100, 5: 500}
fmt.Println(arr4) // [0 100 0 0 0 500 0 0 0 0]
}
3.数组元素遍历
package main
import "fmt"
/*
1、直接通过下标获取元素 arr[index]
2、 0-len i++ 可以使用for循环来结合数组下标进行遍历
3、for range:范围 (new)
*/
func main() {
var arr1 = [5]int{1, 2, 3, 4, 5}
// 错误:index 5 out of bounds [0:5] 数组下标越界
// 数组的长度只有5,你要取出6个元素,不可能取出
//fmt.Println(arr1[5])
fmt.Println("------------------")
// 获取数组的长度 len()
// 下标从0开始,不能<=
for i := 0; i < len(arr1); i++ {
fmt.Println(arr1[i])
}
fmt.Println("------------------")
// goland 快捷方式 数组.for,未来循环数组、切片很多时候都使用for range
// 就是将数组进行自动迭代。返回两个值 index、value
// 注意点,如果只接收一个值,这个时候返回的是数组的下标
// 注意点,如果只接收两个值,这个时候返回的是数组的下标和下标对应的值
for _, value := range arr1 {
fmt.Println(value)
}
}
4.数组排序
arr := [6]int{1,2,3,4,5,0}
// 升序 ASC : 从小到大 0,1,2,3,4,5 A-Z 00:00-24:00
// 降序 DESC : 从大到小 5,4,3,2,1,0
数组的排序,一组数是乱序的,我们如何将它按照升序或者降序排列。
排序算法:冒泡排序、插入排序、选择排序、希尔、堆、快排…
package main
import "fmt"
// 冒泡:每次筛选出一个最大或者最小的数.
/*
index 0 1 2 3 4
value 12 99 79 48 55
*/
// 冒泡排序逻辑,两两比较,大的往后移或者前移。 大
// 第一轮 : 12 79 48 55 99 // 5
// 第二轮 : 12 48 55 79 99 // 4
// 第三轮 : 12 48 55 79 99 // 3 //
// 第四轮 : 12 48 55 79 99 //
// 第五轮 : 12 48 55 79 99
// 代码实践
/*
// 两个数判断,如果一个数大,则交换位置,大放到后面
if arr[x] > arr[x+1] {
arr[x], arr[x+1] = arr[x+1],arr[x]
}
// 多轮判断,for, 循环次数 【数组大小】
*/
func main() {
arr := [...]int{12, 99, 79, 48, 55, 1, 110, 111, 23, 52, 354, 2, 3412, 3, 12, 31}
fmt.Println("初始数组:", arr)
// 冒泡排序
// 1、多少轮
for i := 1; i < len(arr); i++ {
// 2、筛选出来最大数字以后,我们下次不需要将它再计算了
for j := 0; j < len(arr)-i; j++ {
// 比较 // 改变升降序只需要改变符号即可
if arr[j] < arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
5.多维数组
package main
import "fmt"
func main() {
// 定义一个多维数组 二维
arr := [3][4]int{
{0, 1, 2, 3}, // arr[0] //数组
{4, 5, 6, 7}, // arr[1]
{8, 9, 10, 11}, // arr[2]
}
// 二维数组,一维数组存放的是一个数组
fmt.Println(arr[0])
// 要获取这个二维数组中的某个值,找到对应一维数组的坐标,arr[0] 当做一个整体
fmt.Println(arr[0][1])
fmt.Println("------------------")
// 如何遍历二维数组
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Println(arr[i][j])
}
}
// for range
for i, v := range arr {
fmt.Println(i, v)
}
}
二、切片
1.什么是切片
在Go语言中,切片(slice)是对数组一个片段的引用。切片不需要指定固定的大小,而是可以动态调整。切片的声明形式如下:
var 切片名 []元素类型
或者使用 make
函数来创建切片:
切片名 := make([]元素类型, 长度, 容量)
以下是一些关于切片的重要概念:
-
切片声明和初始化:
// 使用数组创建切片 myArray := [5]int{1, 2, 3, 4, 5} mySlice := myArray[1:4] // 创建一个包含 myArray[1] 到 myArray[3] 的切片
// 使用 make 函数创建切片 mySlice := make([]int, 3, 5) // 创建一个长度为3,容量为5的切片
-
切片的长度和容量:
- 长度(
len
)是切片中元素的实际数量。 - 容量(
cap
)是切片底层数组的大小。
- 长度(
-
切片的操作:
- 添加元素:使用
append
函数可以在切片的末尾添加元素。
mySlice = append(mySlice, 6)
- 切片的切割:
newSlice := mySlice[1:3]
- 添加元素:使用
-
切片是引用类型:
修改切片会影响底层数组,因为切片本身不存储数据,而是引用底层数组的一部分。 -
空切片:
空切片表示一个长度和容量都为0的切片,可以用nil
表示。
var emptySlice []int // 空切片,等同于 nil
切片在Go语言中广泛用于动态数据集合的处理,相比数组更灵活,并且可以更方便地进行操作和管理内存。
2.切片遍历
package main
import "fmt"
func main() {
s1 := make([]int, 0, 5)
fmt.Println(s1)
// 切片扩容,append()
s1 = append(s1, 1, 2)
fmt.Println(s1)
// 问题:容量只有5个,那能放超过5个的吗? 可以,切片是会自动扩容的。
s1 = append(s1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7)
fmt.Println(s1)
// 切片扩容之引入另一个切片。
// new : 解构 slice.. ,解出这个切片中的所有元素。
s2 := []int{100, 200, 300, 400}
// slice = append(slice, anotherSlice...)
// ... 可变参数 ...xxx
// [...] 根据长度变化数组的大小定义
// anotherSlice... , slice...解构,可以直接获取到slice中的所有元素
// s2... = {100,200,300,400}
s1 = append(s1, s2...)
// 遍历切片
for i := 0; i < len(s1); i++ {
fmt.Println(s1[i])
}
for i := range s1 {
fmt.Println(s1[i])
}
}
3.使用数组创建切片
package main
import "fmt"
// 切片本身是不保存数据的,只是指向了底层数组。
func main() {
// 数组 [0,10) 数组的截取 [start,end]
// 数组
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 0xc000018230
// 通过数组来创建切片 [start,end)
/*
arr = 10个数
1, 0xc000000001
2, 0xc000000002
3, 0xc000000003
4, 0xc000000004
5, 0xc000000005
*/
s1 := arr[:5] // 1-5 // 0xc000018230 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s2 := arr[3:8] // 4-8 // 0xc000018248
s3 := arr[5:] // 6-10 // 0xc000018258
s4 := arr[:] // 0-10 // 0xc000018230
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
// 查看容量和长度
fmt.Printf("s1 len:%d, cap:%d\n", len(s1), cap(s1)) // s1 len:5, cap:10
fmt.Printf("s2 len:%d, cap:%d\n", len(s2), cap(s2)) // s2 len:5, cap:7
fmt.Printf("s3 len:%d, cap:%d\n", len(s3), cap(s3)) // s3 len:5, cap:5
fmt.Printf("s4 len:%d, cap:%d\n", len(s4), cap(s4)) // s4 len:10, cap:10
// 内存地址
fmt.Printf("%p\n", s1) // 指向了原数组
fmt.Printf("%p\n", s2) // 截断了后,指向了最新截断的元素的下标开始
fmt.Printf("%p\n", s3)
fmt.Printf("%p\n", &arr[5])
fmt.Printf("%p\n", s4) // 指向了原数组
fmt.Printf("%p\n", &arr)
// 修改数组的内容, 切片也随之发生了变化 (切:切片不保存数据-->底层的数组 )
arr[2] = 100
fmt.Println(arr) // [1 2 100 4 5 6 7 8 9 10]
fmt.Println(s1) // [1 2 100 4 5]
fmt.Println(s2) // [4 5 6 7 8]
fmt.Println(s3) // [6 7 8 9 10]
fmt.Println(s4) // [1 2 100 4 5 6 7 8 9 10]
fmt.Println("------------------------")
// 修改切片的内容,发现数组也随之发生了变化。(本质:修改的都是底层的数组)
s2[2] = 80
fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10]
fmt.Println(s1) // [1 2 100 4 5]
fmt.Println(s2) // [4 5 80 7 8]
fmt.Println(s3) // [80 7 8 9 10]
fmt.Println(s4) // [1 2 100 4 5 80 7 8 9 10]
fmt.Println("------------------------")
// 切片扩容
s1 = append(s1, 11, 12, 13, 14, 15, 16)
fmt.Println(arr) // [1 2 100 4 5 80 7 8 9 10]
// 如果在切片容量内加,会导致原来的数组数据发生修改。只有扩容之后,才会导致底层数组会产生拷贝。新的数组
fmt.Println(s1) // [1 2 100 4 5 11 12 13 14 15 16]
fmt.Println(s2) // [4 5 80 7 8]
fmt.Println(s3) // [80 7 8 9 10]
// 查看内存地址,已经不是同一个了
fmt.Printf("%p\n", s1) // 0xc0000d2000
fmt.Printf("%p\n", &arr) // 0xc0000ac0f0
}
4.深浅拷贝
深拷贝:拷贝是数据的本身
- 值类型的数据,默认都是深拷贝,array、int、float、string、bool、struct…
浅拷贝:拷贝是数据的地址,会导致多个变量指向同一块内存。
- 引用类型的数据: slice、map
- 因为切片是引用类的数据,直接拷贝的是这个地址
切片怎么实现深拷贝 copy
package main
import "fmt"
// 切片实现深拷贝
func main() {
// 将原来切片中的数据拷贝到新切片中
s1 := []int{1, 2, 3, 4}
s2 := make([]int, 0) // len:0 cap:0
for i := 0; i < len(s1); i++ {
s2 = append(s2, s1[i])
}
fmt.Println(s1)
fmt.Println(s2)
s1[0] = 100
fmt.Println(s1)
fmt.Println(s2)
// copy
s3 := []int{5, 6, 7}
fmt.Println(s2)
fmt.Println(s3)
// 将s3中的元素拷贝到s2中
//copy(s2, s3)
// 将s2中的元素拷贝到s3中
copy(s3, s2)
fmt.Println(s2)
fmt.Println(s3)
}
三、map
1.什么是map
在Go语言中,map
是一种集合类型,用于存储键值对。map
提供了一种通过键快速查找值的机制。map
的声明和初始化形式如下:
// 使用 make 函数创建一个空的 map
myMap := make(map[键的类型]值的类型)
// 声明并初始化一个 map
myMap := map[键的类型]值的类型{
键1: 值1,
键2: 值2,
// ...
}
以下是一些关于 map
的重要概念:
-
键值对:
map
存储的是键值对,其中每个键必须是唯一的。- 键和值的类型可以是任意类型,只要它们满足
==
操作符的要求。
-
map 的初始化:
- 使用
make
函数创建一个空的 map。 - 使用字面量初始化 map。
- 使用
// 示例:创建和初始化 map
ages := make(map[string]int)
ages["Alice"] = 25
ages["Bob"] = 30
// 或者使用字面量初始化
ages := map[string]int{
"Alice": 25,
"Bob": 30,
// ...
}
-
访问和修改 map:
- 使用键来访问值:
value := myMap[key]
- 使用键来修改值:
myMap[key] = newValue
- 使用键来访问值:
-
删除元素:
- 使用
delete
函数删除 map 中的元素:delete(myMap, key)
- 使用
// 示例:删除 map 中的元素
delete(ages, "Alice")
- 判断键是否存在:
- 使用多返回值的方式判断键是否存在,如果键存在,第二个返回值为
true
,否则为false
。
- 使用多返回值的方式判断键是否存在,如果键存在,第二个返回值为
age, ok := ages["Alice"]
if ok {
// 键存在,可以使用 age
} else {
// 键不存在
}
- 迭代 map:
- 使用
range
关键字可以迭代 map 中的键值对。
- 使用
for key, value := range ages {
fmt.Println(key, value)
}
map
是一个非常方便的数据结构,适用于需要通过键值对进行快速查找的场景。需要注意的是,map
是无序的,每次迭代得到的顺序可能不同。如果需要有序的键值对,可以使用切片来保存键,并排序切片。
2.map与切片结合使用
map结合slice使用
package main
import "fmt"
/*
需求:
1、创建map来存储人的信息,name,age,sex,addr
2、每个map保存一个的信息
3、将这些map存入到切片中
4、打印这些数据
*/
func main() {
user1 := make(map[string]string)
user1["name"] = "xiaoming"
user1["age"] = "27"
user1["sex"] = "男"
user1["addr"] = "重庆"
user2 := make(map[string]string)
user2["name"] = "xiaohong"
user2["age"] = "30"
user2["sex"] = "男"
user2["addr"] = "长沙"
user3 := map[string]string{"name": "小蓝", "age": "18", "sex": "男", "addr": "火星"}
fmt.Println(user3)
// 3个数据有了,存放到切片中,供我们使用
userDatas := make([]map[string]string, 0, 3)
userDatas = append(userDatas, user1)
userDatas = append(userDatas, user2)
userDatas = append(userDatas, user3)
fmt.Println(userDatas)
// 0 map[string]string
for _, user := range userDatas {
//fmt.Println(i)
fmt.Println(user["addr"])
}
}