[TOC]
## 判断切片是否为空
要检查切片是否为空,请始终使用len(s) == 0来判断,而不应该使用s == nil来判断。
## 切片比较
切片之间是不能比较的,只能判断是否为 `nil` 值。
```go
s1 := make([]int, 5)
s2 := s1
// fmt.Println(s1 == s2) // 非法操作。提示invalid operation: cannot compare s1 == s2 (slice can only be compared to nil)
fmt.Println(s2 == nil)
// 运行结果:
// false
```
## 切片遍历
有两个常用方式遍历
```go
s3 := []int{1, 2, 3, 4, 5}
// 方式一
for i := 0; i < len(s3); i++ {
fmt.Printf("s3[%d]: %#v\n", i, s3[i])
}
// 方式二
for i, v := range s3 {
fmt.Printf("s3[%d]: %#v\n", i, v)
}
```
## 切片的赋值拷贝
切片值拷贝,两个变量引用的底层数组是同一个。所以只要修改其中一个变量的元素。则另一个元素也跟着改变(一动则动)
```go
s4 := make([]string, 5)
s4[1] = "guangdong"
s4[4] = "hunan"
for i := 0; i < len(s4); i++ {
fmt.Printf("s4[%d]: %#-12v, pointer: %p\n", i, s4[i], &s4[i])
}
fmt.Printf("s4: %#v, pointer: %p\n\n", s4, s4)
s5 := s4
for i := 0; i < len(s5); i++ {
fmt.Printf("s5[%d]: %#-12v, pointer: %p\n", i, s5[i], &s5[i])
}
fmt.Printf("s5: %#v, pointer: %p\n", s5, s5)
// 运行结果:
// s4[0]: "" , pointer: 0xc000076050
// s4[1]: "guangdong" , pointer: 0xc000076060
// s4[2]: "" , pointer: 0xc000076070
// s4[3]: "" , pointer: 0xc000076080
// s4[4]: "hunan" , pointer: 0xc000076090
// s4: []string{"", "guangdong", "", "", "hunan"}, pointer: 0xc000076050
// s5[0]: "" , pointer: 0xc000076050
// s5[1]: "guangdong" , pointer: 0xc000076060
// s5[2]: "" , pointer: 0xc000076070
// s5[3]: "" , pointer: 0xc000076080
// s5[4]: "hunan" , pointer: 0xc000076090
// s5: []string{"", "guangdong", "", "", "hunan"}, pointer: 0xc000076050
```
![](https://img.kancloud.cn/10/c9/10c99be020f5c2aff76fdba4c54eebd5_1446x994.png)
## 切片copy()函数复制
```go
s6 := make([]int, 5)
s6[1] = 5
s6[3] = 2
for i := 0; i < len(s6); i++ {
fmt.Printf("s6[%d]: %#-12v, pointer: %p\n", i, s6[i], &s6[i])
}
fmt.Printf("s6: %#v, pointer: %p\n\n", s6, s6)
s7 := make([]int, 5)
copy(s7, s6)
for i := 0; i < len(s7); i++ {
fmt.Printf("s7[%d]: %#-12v, pointer: %p\n", i, s7[i], &s7[i])
}
fmt.Printf("s7: %#v, pointer: %p\n", s7, s7)
// 运行结果:
// s6[0]: 0 , pointer: 0xc0000ba000
// s6[1]: 5 , pointer: 0xc0000ba008
// s6[2]: 0 , pointer: 0xc0000ba010
// s6[3]: 2 , pointer: 0xc0000ba018
// s6[4]: 0 , pointer: 0xc0000ba020
// s6: []int{0, 5, 0, 2, 0}, pointer: 0xc0000ba000
// s7[0]: 0 , pointer: 0xc0000ba030
// s7[1]: 5 , pointer: 0xc0000ba038
// s7[2]: 0 , pointer: 0xc0000ba040
// s7[3]: 2 , pointer: 0xc0000ba048
// s7[4]: 0 , pointer: 0xc0000ba050
// s7: []int{0, 5, 0, 2, 0}, pointer: 0xc0000ba030
```
![](https://img.kancloud.cn/9f/3f/9f3f6b36c21203b5334488f2a4a4c4b9_1412x1075.png)
## append添加元素
append内置函数将元素附加到切片的末尾。如果它有足够的容量,则重新划分目标以容纳新元素。如果没有,将分配一个新的底层数组。Append返回更新后的切片。
```go
s8 := make([]string, 5)
for i := 0; i < len(s8); i++ {
fmt.Printf("s8[%d]: %v, pointer: %p\n", i, s8[i], &s8[i])
}
fmt.Println()
s9 := s8[1:3]
for i := 0; i < len(s9); i++ {
fmt.Printf("s9[%d]: %v, pointer: %p\n", i, s9[i], &s9[i])
}
fmt.Printf("s8:{ pointer: %p, len: %d, cap: %d, value: %#v }\n", s8, len(s8), cap(s8), s8)
fmt.Printf("s9:{ pointer: %p, len: %d, cap: %d, value: %#v }\n\n", s9, len(s9), cap(s9), s9)
// 添加单个元素
s9 = append(s9, "jiaxzeng")
for i := 0; i < len(s9); i++ {
fmt.Printf("s9[%d]: %v, pointer: %p\n", i, s9[i], &s9[i])
}
fmt.Printf("s8:{ pointer: %p, len: %d, cap: %d, value: %#v }\n", s8, len(s8), cap(s8), s8)
fmt.Printf("s9:{ pointer: %p, len: %d, cap: %d, value: %#v }\n\n", s9, len(s9), cap(s9), s9)
// 添加多个元素
// 如果需要添加另一个切片里的数据
// 切片解压语法:切片[start:end]...
s9 = append(s9, []string{"golang", "python", "shell"}...)
for i := 0; i < len(s9); i++ {
fmt.Printf("s9[%d]: %v, pointer: %p\n", i, s9[i], &s9[i])
}
fmt.Printf("s8:{ pointer: %p, len: %d, cap: %d, value: %#v }\n", s8, len(s8), cap(s8), s8)
fmt.Printf("s9:{ pointer: %p, len: %d, cap: %d, value: %#v }\n\n", s9, len(s9), cap(s9), s9)
// 运行结果:
// s8[0]: , pointer: 0xc000076050
// s8[1]: , pointer: 0xc000076060
// s8[2]: , pointer: 0xc000076070
// s8[3]: , pointer: 0xc000076080
// s8[4]: , pointer: 0xc000076090
// s9[0]: , pointer: 0xc000076060
// s9[1]: , pointer: 0xc000076070
// s8:{ pointer: 0xc000076050, len: 5, cap: 5, value: []string{"", "", "", "", ""} }
// s9:{ pointer: 0xc000076060, len: 2, cap: 4, value: []string{"", ""} }
// s9[0]: , pointer: 0xc000076060
// s9[1]: , pointer: 0xc000076070
// s9[2]: jiaxzeng, pointer: 0xc000076080
// s8:{ pointer: 0xc000076050, len: 5, cap: 5, value: []string{"", "", "", "jiaxzeng", ""} }
// s9:{ pointer: 0xc000076060, len: 3, cap: 4, value: []string{"", "", "jiaxzeng"} }
// s9[0]: , pointer: 0xc00010e000
// s9[1]: , pointer: 0xc00010e010
// s9[2]: jiaxzeng, pointer: 0xc00010e020
// s9[3]: golang, pointer: 0xc00010e030
// s9[4]: python, pointer: 0xc00010e040
// s9[5]: shell, pointer: 0xc00010e050
// s8:{ pointer: 0xc000076050, len: 5, cap: 5, value: []string{"", "", "", "jiaxzeng", ""} }
// s9:{ pointer: 0xc00010e000, len: 6, cap: 8, value: []string{"", "", "jiaxzeng", "golang", "python", "shell"} }
```
![](https://img.kancloud.cn/3a/5d/3a5d72461e64e6fed913797e938f0d1e_1393x1328.png)
>[info] 总结:
> - 当扩容底层数组容量足够时,append 添加元素是在原来的数组上操作。
> - 当扩容底层数组容量且不足且容量小于1024个元素时>
- `扩容元素 + 原来长度` 小于 原来数组的两倍时,append添加元素是copy原来数组到其他内存上并且容量扩容成原来数组的两倍个元素。
- `扩容元素 + 原来长度` 大于 原来数组的两倍时,append添加元素是copy原来数组到其他内存上并且容量扩容成 `扩容元素 + 原来长度` 个元素。
> - 当扩容底层数组容量且不足且容量大于1024个元素时。append添加元素是copy原来数组到其他内存上并且容量扩容增加( 原容量*0.25 的倍数) 直至足够存下新扩容的所有元素。
## 切片删除元素
Golang语言没有remove函数,但是可以曲线救国来实现删除动作。通过上面append函数来操作。
切片切割分成需要的元素,然后用append加起来就是返回值就是目标切片。
语法:append(切片[:删除的下标], 切片[删除的下标+2:]...)
```go
s10 := []string{"golang", "python", "", "shell"}
fmt.Printf("s10: %#v\n", s10)
// 删除下标为2的元素
// 第一个参数是保留需要的切片。
// 第二个参数时是将后面数据变成切片,然后再切片解压
s10 = append(s10[:2], s10[3:]...)
fmt.Printf("s10: %#v\n", s10)
```
## 思考
以下几个printf输出什么结果
```go
a1 := [5]string{"golang", "python", "java"}
s11 := a1[:2]
s12 := a1[2:]
s13 := a1[:]
s14 := append(s13, []string{"c", "c++"}...)
a1[1] = "shell"
fmt.Printf("s11: %#v\n", s11)
fmt.Printf("s12: %#v\n", s12)
fmt.Printf("s13: %#v\n", s13)
fmt.Printf("s14: %#v\n", s14)
```
- Golang简介
- 开发环境
- Golang安装
- 编辑器及快捷键
- vscode插件
- 第一个程序
- 基础数据类型
- 变量及匿名变量
- 常量与iota
- 整型与浮点型
- 复数与布尔值
- 字符串
- 运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 流程控制语句
- 获取用户输入
- if分支语句
- for循环语句
- switch语句
- break_continue_goto语法
- 高阶数据类型
- pointer指针
- array数组
- slice切片
- slice切片扩展
- map映射
- 函数
- 函数定义和调用
- 函数参数
- 函数返回值
- 作用域
- 函数形参传递
- 匿名函数
- 高阶函数
- 闭包
- defer语句
- 内置函数
- fmt
- strconv
- strings
- time
- os
- io
- 文件操作
- 编码
- 字符与字节
- 字符串
- 读写文件
- 结构体
- 类型别名和自定义类型
- 结构体声明
- 结构体实例化
- 模拟构造函数
- 方法接收器
- 匿名字段
- 嵌套与继承
- 序列化
- 接口
- 接口类型
- 值接收者和指针接收者
- 类型与接口对应关系
- 空接口
- 接口值
- 类型断言
- 并发编程
- 基本概念
- goroutine
- channel
- select
- 并发安全
- 练习题
- 第三方库
- Survey
- cobra