[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
```

## 切片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
```

## 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"} }
```

>[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