一、数组
(1) 数组可以存放多个同一类型数据。数组也是一种数据类型,在
Go
中,数组是值类型。
二、定义一个数组
var Arr [2] float64
Arr[0] = 1.2
Arr[1] = 1.3
1 数组的定义
func goarr() {
// 定义一个数组
// var 数组名 [数组大小]数据类型
var arr [3]int // 2个容量,int类型
fmt.Println("地址0是:", &arr[0]) // 0x1400001a0a8
arr[0] = 1 // 赋值
arr[1] = 2
arr[2] = 3
fmt.Println(arr)
fmt.Println("地址1是:", &arr[0]) // 0x1400001a0a8
fmt.Println("地址2是:", &arr[1]) // 0x1400001a0b0
fmt.Println("地址2是:", &arr[2]) // 0x1400001a0b8
}
2 数组在内存布局(重要)
![](https://img-blog.csdnimg.cn/direct/fd96a63faf8d46898169f011e9211272.png)
a. 每个每个空间占用的字节数取决于数组类型
b. 数组在内存中是连续的
三、数组的使用
数组名
[
下标
]
比如:你要使用
a
数组的第三个元素
a[2]
// 数组的定义
func goarr1() {
// 第一种
var arr [3]int // 2个容量,int类型
arr[0] = 1 // 赋值
arr[1] = 2
arr[2] = 3
// 第二种
var arr1 [3]int = [3]int{4, 5, 6}
// 第三种
var arr2 = [3]int{7, 8, 9}
// 第四种可变
var arr3 = [...]int{10, 11, 12}
// 第五种
arr4 := [3]string{"a", "b", "c"}
// 第六种
var arr5 = [3]int{1: 10, 0: 11, 2: 12}
fmt.Println(arr1, arr2, arr3, arr4, arr5)
}
四、数组的遍历
(1) for i := 0; i <= len(Arr); i++ {}
(2) for index, val := range arr {} index数组的下标,val对应的值,不需要用到下标时用_忽略
五、数组的使用
1)
数组是多个相同类型的数据
,
一个数组一旦声明
/
定义了
,
其长度是固定的
,
不能动态变化
2) var arr []int
这时
arr
就是一个
slice
切片
3)
数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
4)
数组创建后,如果没有赋值,有默认值
(
零值
)
数值类型数组:默认值为
0
字符串数组:
默认值为
""
bool
数组: 默认值为
false
5)
使用数组的步骤
1.
声明数组并开辟空间
2
给数组各个元素赋值
(
默认零值
) 3
使用数组
6)
数组的下标是从
0
开始的
7)
数组下标必须在指定范围内使用,否则报
panic
:数组越界,比如var arr [5]int 则有效下标为 0-4
8) Go
的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响
9)
如想在其它函数中,去修改原来的数组,可以使用引用传递
(
指针方式
)
10)
长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度
六、切片
1)
切片是引用类型
,遵循引用传递。
2)
切片的
使用和数组类似
,遍历切片、访问切片的元素和求切片长度
len(slice)
都一样。
3)
切片的长度可变,因此切片是一个
可以动态变化数组
。
4)
切片定义的基本语法
:
var
切片名
[]
类型
比如:
var a [] int
func goslice() {
// 定义一个切片
arr := [6]int{1, 2, 3, 4, 5, 6}
slice := arr[1:3] // 2, 3 左闭右开,包含1不包含3
clice2 := arr[2:4]
fmt.Println(slice, "切片的长度是:", len(slice), "切片的容量是:", cap(slice))
fmt.Println("切片地址1:", &slice[0], "地址2:", &slice[1]) // 切片地址1: 0x140000b4038 地址2:0x140000b4040
fmt.Println("原数组地址1:", &arr[1], "数组2:", &arr[2]) // 原数组地址1: 0x140000b4038 数组2: 0x140000b4040
// 切片的底层其实就是一个数组
goEdit(slice)
fmt.Println(slice) // [2 12] 切片是引用传值
fmt.Println(clice2) // [13 4]
}
func goEdit(slice []int) {
slice[1] = 13
}
![](https://img-blog.csdnimg.cn/direct/ed9d1f465ae74748b4f4a012de1dd631.png)
1. slice
的确是一个引用类型
2. slice
从底层来说,其实就是一个数据结构
(struct
结构体
)
type slice struct {
ptr *[2]int
len int
cap int
}
七、切片的创建
// 切片的创建类型
func sliceCreate() {
// 1 基于数组
month := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
q2 := month[3:6] // [4 5 6] 第二季度
summer := month[5:8] // 夏季
all := month[:] // 全年
firsthalf := month[:6] // 上半年
secondhalf := month[6:] // 下半年
fmt.Println("第二季度:", q2) // [4 5 6]
fmt.Println("夏季:", summer) // [6 7 8]
fmt.Println("全年:", all)
fmt.Println("上半年年:", firsthalf) // [1 2 3 4 5 6]
fmt.Println("下半年年:", secondhalf)
// 2 基于切片再切一刀
q1 := firsthalf[0:3]
q3 := firsthalf[6:9] // 这里可以看出本质上还是基于底层的数组
fmt.Println("基于切片第一季度:", q1, "基于切片第三季度:", q3) // 基于切片第一季度: [1 2 3] 基于切片第三季度: [7 8 9]
// 3 make 创建切片
mySlice1 := make([]int, 5) // 初始长度为5的切片
mySlice2 := make([]int, 5, 10) // 长度5 容量10
mySlice3 := []int{1, 2, 3, 4, 5} // 长度5 容量5
fmt.Println(mySlice1, mySlice2, mySlice3) // 0 0 0 0 0] [0 0 0 0 0] [1 2 3 4 5]
}
make创建基本语法:
var
切片名
[]type = make([]type, len, [cap])
参数说明
: type:
数据类型
len :
大小
cap
:指定切片容量,
可选, 如果你分配了
cap,
则要
求
cap>=len.
1)
通过
make
方式创建切片可以指定切片的大小和容量
2)
如果没有给切片的各个元素赋值,那么就会使用默认值
[int , float=> 0 string =>”” bool =>false]
3)
通过
make
方式创建的切片对应的数组是由
make
底层维护,对外不可见,即只能通过
slice
去访问各个元素
七、切片的遍历
mySlice3 := []int{1, 2, 3, 4, 5} // 长度5 容量5
for i := 0; i < len(mySlice3); i++ {
fmt.Println(mySlice3[i])
}
for k, val := range mySlice3 {
fmt.Println(k, "----", val)
}
八、切片的使用细节
1)
切片初始化时
var
autumn
=
month
[
8
:
11
]
下标从0开始,左开右闭,包含第8个,不包含第11个
2)
切片初始化时,仍然不能越界。范围在
[0-len(arr)]
之间,但是可以动态增长
.
相关缩写
all := month[:] // 全年
firsthalf := month[:6] // 上半年
secondhalf := month[6:] // 下半年
3) cap
是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
4)
切片可以继续切片
firsthalf := month[:6] // 上半年
// 基于切片再切一刀
q1 := firsthalf[0:3]
q3 := firsthalf[6:9] // 这里可以看出本质上还是基于底层的数组
fmt.Println("基于切片第一季度:", q1, "基于切片第三季度:", q3) // 基于切片第一季度: [1 2 3] 基于切片第三季度: [7 8 9]
6)
用
append
内置函数,可以对切片进行动态追加
// 切片追加
func sliceAppend() {
var slice3 = make([]int, 4, 4)
slice3[0] = 100
slice3[1] = 200
slice3[2] = 300
newSlice := append(slice3, 400, 500) // append() 函数向切片追加新元素
appendSlice := []int{21, 22, 23} // 长度和容量会自动扩容
newSlice2 := append(slice3, appendSlice...) // 注意末尾的 ... 不能省略
fmt.Println(slice3) // [100 200 300 0]
fmt.Println(newSlice) // [100 200 300 0 400 500]
fmt.Println(newSlice2) // [100 200 300 0 21 22 23]
fmt.Println("slice3[0]:", &slice3[0], "地址new[0]:", &newSlice[0], "地址Slice2[0]:", &newSlice2[0])
// 地址oldslice3[0]: 0x140000b8020 地址new[0]: 0x140000a0080 地址Slice2[0]: 0x140000a00c0 说明不在同一个数组中啦
}
![](https://img-blog.csdnimg.cn/direct/5be0c3ec9288421e86f0757be6c92da1.png)
切片
append
操作的底层原理分析
:
切片
append
操作的本质就是对数组扩容 go 底层会创建一下新的数组 newArr将 slice 原来包含的元素拷贝到新的数组 newArr slice 重新引用到 newArr 注意 newArr 是在底层来维护的,程序员不可见
7)
切片的拷贝操作
// 拷贝
func sliceCopy() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := make([]int, 3, 5)
copy(slice2, slice1)
fmt.Println(slice1, slice2) // [1 2 3 4 5] [1 2 3]
fmt.Println(&slice1[0], &slice2[0]) // 0x140000b40c0 0x140000b40f0
}
(1) copy(para1, para2)
参数的数据类型是切片
(2) 拷贝出来的与原来的首地址不一样,说明底层也拷贝了一个新的数组
8)
切片是引用类型,所以在传递时,遵守引用传递机制。
九、string 和 slice
1) string
底层是一个
byte
数组,因此
string
也可以进行切片处理
// 字符串的底层也是个切片
func strSlice() {
str := "707217@qq.com"
slice1 := str[3:7]
fmt.Println(slice1) // 217@
}
2) string
和切片在内存的形式,以 “abcd"
![](https://img-blog.csdnimg.cn/direct/0ef1717546914e77bdd94773f7ae0a41.png)
3) string
是不可变的,也就说不能通过
str[0] = 'z'
方式来修改字符串
// arr1[1] = '
京
' //
非字符报错
4)
如果需要修改字符串,可以先将
string -> []byte /
或者
[]rune ->
修改
->
重写转成
string
// 字符串的底层也是个切片
func strSlice() {
str := "7072北17@qq.com"
slice1 := str[3:7]
fmt.Println(slice1) // 217@
//slice2 := str[:]
//slice2[0] = "京" // 报错,不能这么操作
arr1 := []byte(str)
fmt.Println(arr1) // [55 48 55 50 229 140 151 49 55 64 113 113 46 99 111 109]
arr1[0] = 'z'
// arr1[1] = '京' // 非字符报错
fmt.Println(string(arr1)) // z072北17@qq.com
arr2 := []rune(str)
fmt.Println(arr2)
arr2[0] = '京'
str = string(arr2)
fmt.Println(str) // 京072北17@qq.com
}
十、切片深入理解(巩固一下)
var arr = [3]int{1, 2, 3} // 这是个数组
var slice = []int{1, 2, 3} // 这是个切片
![](https://img-blog.csdnimg.cn/direct/0c42f273753e40218ab3ac6ee70b0f20.png)
切片的创建
1)基于数组创建
切片的下标是
左闭右开
// 从数组中,切一点出来,变成切片
month := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
q2 := month[3:6] // 第二季度 包含第三个 不包含第6个
summer := month[5:8] // 夏季
all := month[:] // 全年
firsthalf := month[:6] // 上半年
secondhalf := month[6:] // 下半年
fmt.Println(q2) // [4 5 6]
fmt.Println(summer) // [6 7 8]
fmt.Println(all) // [1 2 3 4 5 6 7 8 9 10 11 12]
fmt.Println(firsthalf) // [1 2 3 4 5 6]
fmt.Println(secondhalf) // [7 8 9 10 11 12]
month := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
all := month[:] // 全年
all[11] = 100 // 这里会把数组和切片的值改掉
fmt.Println(month)
fmt.Println(all)
图解
arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
q2 := arr[3:6] // 第二季度 包含第三个 不包含第6个
fmt.Println(len(q2)) // 3
fmt.Println(cap(q2)) // 5
![](https://img-blog.csdnimg.cn/direct/28e06c76b75849e09bc18bac9ba199c7.png)
基于切片创建
month := [12]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
firsthalf := month[:6] // 上半年
fmt.Println(len(firsthalf)) // 6
fmt.Println(cap(firsthalf)) // 12
// 基于切片再切一刀
q2 := firsthalf[3:6]
q3 := firsthalf[6:9] // 这里可以看出本质上还是基于底层的数组
fmt.Println(q2) // [4 5 6]
fmt.Println(q3) // [7 8 9]
make()创建切片
// make 创建切片
mySlice1 := make([]int, 5) // 初始长度为5的切片
mySlice2 := make([]int, 5, 10) // 长度5 容量10
mySlice3 := []int{1, 2, 3, 4, 5} // 长度5 容量5
for i := 0; i < len(mySlice1); i++ {
fmt.Println(mySlice1[i]) // 0 0 0 0 0
}
for index, val := range mySlice3 {
fmt.Println(index, "--", val, "&&") // 0 -- 1 && 1 -- 2 && 2 -- 3 && 3 -- 4 && 4 -- 5 &&
}
fmt.Println(mySlice2) // 0 0 0 0 0
切片扩容
var oldSlice = make([]int, 5, 10)
newSlice := append(oldSlice, 1, 2, 3) // append() 函数向切片追加新元素
appendSlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} // 长度和容量会自动扩容
Slice2 := append(oldSlice, appendSlice...) // 注意末尾的 ... 不能省略
fmt.Println(oldSlice) // [0 0 0 0 0]
fmt.Println(newSlice) // [0 0 0 0 0 1 2 3]
fmt.Println(Slice2) //[0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12]
切片的删除(
其实是通过切片的切片实现的「伪删除」)
slice3 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
slice3 = slice3[:len(slice3)-5] // 删除 slice3 尾部 5 个元素
fmt.Println(slice3, len(slice3), cap(slice3)) // [1 2 3 4 5] 5 10
slice3 = slice3[5:] // 删除 slice3 头部 5 个元素
fmt.Println(slice3, len(slice3), cap(slice3)) // [] 0 5
切片对应的结构体
type slice struct {
array unsafe.Pointer //指向存放数据的数组指针
len int //长度有多大
cap int //容量有多大
}