數組
- 和C語言一樣,Go語言中也有數組的概念, Go語言中的數組也是用于保存一組
相同類型
的數據 - 和C語言一樣,Go語言中的數組也分為
一維數組
和多維數組
一維數組
- 格式:
var arr [元素個數]數據類型
- 和C語言中數組不同, Go語言中數組定義之后就
有默認的初始值
- 默認初始值就是保存數據類型的默認值(零值)
package main import "fmt" func main() { // 1.定義一個數組 var arr [3]int // 2.打印數組 fmt.Println(arr) //[0 0 0] // 1.定義一個數組 var arr [3]bool // 2.打印數組 fmt.Println(arr) //[false false false] }
- 和C語言一樣,Go語言中的數組也提供了好幾種初始化方式
package main import "fmt" func main() { // 1.定義的同時完全初始化 var arr1 [3]int = [3]int{1, 3, 5} // 2.打印數組 fmt.Println(arr1) // [1 3 5] // 1.定義的同時部分初始化 var arr4 [3]int = [3]int{8, 9} // 2.打印數組 fmt.Println(arr4) // [8 9 0] // 1.定義的同時指定元素初始化 var arr5 [3]int = [3]int{0:8, 2:9} // 2.打印數組 fmt.Println(arr5) // [8 0 9] // 1.先定義再逐個初始化 var arr3 [3]int arr3[0] = 1 arr3[1] = 2 arr3[2] = 3 // 2.打印數組 fmt.Println(arr3) // [1 2 3] }
- 和C語言中的數組不同,Go語言中數組除了可以定義的同時初始化以外,還
可以先定義再一次性初始化
package main import "fmt" func main() { // 1.先定義再一次性初始化 var arr2 [3]int arr2 = [3]int{2, 4, 6} // 2.打印數組 fmt.Println(arr2) // [2 4 6] }
- 和C語言一樣,Go語言中如果定義數組的同時初始化,那么元素個數可以省略,但是必須使用
...
來替代- ...會根據初始化元素個數自動確定數組長度
package main import "fmt" func main() { // 1.定義的同時完全初始化 var arr1 = [...]int{1, 3, 5} // 2.打印數組 fmt.Println(arr1) // [1 3 5] // 1.定義的同時指定元素初始化 var arr2 = [...]int{6:5} // 2.打印數組 fmt.Println(arr2) // [0 0 0 0 0 0 5] }
- 和C語言中數組不同, Go語言中數組定義之后就
- Go語言中數組的訪問和使用方式和C語言一樣都是通過
數組名稱[索引]
的方式
package main
import "fmt"
func main() {
arr := [...]int{1, 3, 5}
// 使用數組, 往數組中存放數據
arr[1] = 666
// 訪問數組, 從數組中獲取數據
fmt.Println(arr[0])
fmt.Println(arr[1])
fmt.Println(arr[2])
}
- 遍歷數組
- Go語言中提供了兩種遍歷數組的方式, 一種是通過傳統for循環遍歷, 一種是通過for...range循環遍歷
package main
import "fmt"
func main() {
arr := [...]int{1, 3, 5}
// 傳統for循環遍歷
for i:=0; i<len(arr); i++{
fmt.Println(i, arr[i])
}
// for...range循環遍歷
for i, v := range arr{
fmt.Println(i, v)
}
}
- 數組注意點
- Go語言中
數組長度
也是數據類型的一部分
package main import "fmt" func main() { var arr1 [3]int var arr2 [3]int //var arr3 [2]int fmt.Println(arr1 == arr2) // true //fmt.Println(arr1 == arr3) // 編譯報錯, 不是相同類型不能比較 }
- 如果元素類型支持==、!=操作時,那么數組也支持此操作
package main import "fmt" func main() { var arr1 [3]int = [3]int{1, 3, 5} var arr2 [3]int = [3]int{1, 3, 5} var arr3 [3]int = [3]int{2, 4, 6} // 首先會判斷`數據類型`是否相同,如果相同會依次取出數組中`對應索引的元素`進行比較, // 如果所有元素都相同返回true,否則返回false fmt.Println(arr1 == arr2) // true fmt.Println(arr1 == arr3) // false }
- Go語言中的數組是值類型, 賦值和傳參時會復制整個數組
package main import "fmt" func main() { var arr1 [3]int = [3]int{1, 3, 5} var arr2 [3]int = arr1 arr2[0] = 666 fmt.Println(arr1) // [1 3 5] fmt.Println(arr2) // [666 3 5] }
- Go語言中
二維數組
- 用法和C語言數組一樣, 只是創建的格式不同
- 格式:
[行數][列數]類型
package main
import "fmt"
func main() {
// 創建一個兩行三列數組
arr := [2][3]int{
{1, 2, 3},
{4, 5, 6}, //注意: 數組換行需要以逗號結尾
}
fmt.Println(arr)// [[1 2 3] [4 5 6]]
}
- 創建多維數組時只允許第一維度使用...
- 格式:
[...][列數]類型
package main
import "fmt"
func main() {
// 創建一個兩行三列數組
arr := [...][3]int{
{1, 2, 3},
{4, 5, 6},
}
fmt.Println(arr)// [[1 2 3] [4 5 6]]
}
切片
- 無論是C語言中的數組還是Go語言中的數組,數組的長度一旦確定就不能改變, 但在實際開發中我們可能事先不能確定數組的長度, 為了解決這類問題Go語言中推出了一種新的數據類型
切片
- 切片可以簡單的理解為長度可以變化的數組, 但是Go語言中的切片本質上是一個結構體
- 切片源碼
type slice struct{ array unsafe.Pointer // 指向底層數組指針 len int // 切片長度(保存了多少個元素) cap int // 切片容量(可以保存多少個元素) }
- 切片創建的三種方式
- 方式一: 通過數組創建切片
array[startIndex:endIndex]
package main import "fmt" func main() { var arr = [5]int{1, 3, 5, 7, 9} // 從數組0下標開始取,一直取到2下標前面一個索引 var sce = arr[0:2] fmt.Println(sce) // [1 3] // 切片len = 結束位置 - 開始位置 fmt.Println(len(sce)) // 2 - 0 = 2 fmt.Println(cap(sce)) // 5 - 0 = 5 // 數組地址就是數組首元素的地址 fmt.Printf("%p\n", &arr) // 0xc04200a330 fmt.Printf("%p\n", &arr[0]) // 0xc04200a330 // 切片地址就是數組中指定的開始元素的地址 // arr[0:2]開始地址為0, 所以就是arr[0]的地址 fmt.Printf("%p\n", sce) // 0xc04200a330 }
-
image.png
package main import "fmt" func main() { var arr = [5]int{1, 3, 5, 7, 9} // 根據數組的索引片段創建切片 var sce = arr[2:4] fmt.Println(sce) // [5 7] fmt.Println(len(sce)) // 4 - 2 = 2 fmt.Println(cap(sce)) // 5 - 2 = 3 fmt.Printf("%p\n", &arr[2]) // 0xc042076070 fmt.Printf("%p\n", sce) // 0xc042076070 }
- 指定起始位置時有三種方式可以指定
- 開始位置和結束位置都指定
- 只指定開始位置或結束位置
- 開始位置和結束位置都不指定
package main
import "fmt"
func main() {
var arr = [5]int{1, 3, 5, 7, 9}
// 同時指定開始位置和結束位置
var sce1 = arr[0:2]
fmt.Println(sce1) // [1 3]
// 只指定結束位置
var sce3 = arr[:2]
fmt.Println(sce3) // [1 3]
// 只指定開始位置
var sce2 = arr[0:]
fmt.Println(sce2) // [1 3 5 7 9]
// 都不指定
var sce4 = arr[:]
fmt.Println(sce4) // [1 3 5 7 9]
}
- 方式二: 通過make函數創建
make(類型, 長度, 容量)
- 內部會先創建一個數組, 然后讓切片指向數組
- 如果沒有指定容量,那么容量和長度一樣
package main
import "fmt"
func main() {
// 第一個參數: 指定切片數據類型
// 第二個參數: 指定切片的長度
// 第三個參數: 指定切片的容量
var sce = make([]int, 3, 5)
fmt.Println(sce) // [0 0 0]
fmt.Println(len(sce)) // 3
fmt.Println(cap(sce)) // 5
/*
內部實現原理
var arr = [5]int{0, 0, 0}
var sce = arr[0:3]
*/
}
- 方式三:通過Go提供的語法糖快速創建
- 和創建數組一模一樣, 但是
不能指定長度
- 通過該方式創建時, 切片的
長度和容量相等
- 和創建數組一模一樣, 但是
package main
import "fmt"
func main() {
var sce = []int{1, 3, 5}
fmt.Println(sce) // [1 3 5]
fmt.Println(len(sce)) // 3
fmt.Println(cap(sce)) // 3
}
-
切片的使用
- 切片的基本使用方式和數組一樣, 可以通過
切片名稱[索引]
方式操作切片
package main import "fmt" func main() { var sce = []int{1, 3, 5} // 使用切片, 往切片中存放數據 sce[1] = 666 // 訪問切片, 從切片中獲取數據 fmt.Println(sce) // [1 666 5] }
- 和數組一樣, 如果通過
切片名稱[索引]
方式操作切片, 不能越界
package main import "fmt" func main() { var sce = []int{1, 3, 5} // 編譯報錯, 越界 sce[3] = 666 }
- 如果希望切片自動擴容,那么添加數據時必須使用append方法
- append函數會在切片
末尾
添加一個元素, 并返回一個追加數據之后的切片 - 利用append函數追加數據時,如果追加之后沒有超出切片的容量,那么返回原來的切片, 如果追加之后超出了切片的容量,那么返回一個新的切片
- append函數每次給切片擴容都會按照原有切片容量*2的方式擴容
- append函數會在切片
package main import "fmt" func main() { var sce = []int{1, 3, 5} fmt.Println("追加數據前:", sce) // [1 3 5] fmt.Println("追加數據前:", len(sce)) // 3 fmt.Println("追加數據前:", cap(sce)) // 3 fmt.Printf("追加數據前: %p\n", sce) // 0xc0420600a0 // 第一個參數: 需要把數據追加到哪個切片中 // 第二個參數: 需要追加的數據, 可以是一個或多個 sce = append(sce, 666) fmt.Println("追加數據后:", sce) // [1 3 5 666] fmt.Println("追加數據后:", len(sce)) // 4 fmt.Println("追加數據后:", cap(sce)) // 6 fmt.Printf("追加數據前: %p\n", sce) // 0xc042076b60 }
- 除了append函數外,Go語言還提供了一個copy函數, 用于兩個切片之間數據的快速拷貝
- 格式:
copy(目標切片, 源切片)
, 會將源切片中數據拷貝到目標切片中
- 格式:
package main import "fmt" func main() { var sce1 = []int{1, 3, 5} var sce2 = make([]int, 5) fmt.Printf("賦值前:%p\n", sce1) // 0xc0420600a0 fmt.Printf("賦值前:%p\n", sce2) // 0xc042076060 // 將sce2的指向修改為sce1, 此時sce1和sce2底層指向同一個數組 sce2 = sce1 fmt.Printf("賦值后:%p\n", sce1) // 0xc0420600a0 fmt.Printf("賦值后:%p\n", sce2) // 0xc0420600a0 //copy(sce2, sce1) fmt.Println(sce1) // [1 3 5] fmt.Println(sce2) // [1 3 5] sce2[1] = 666 fmt.Println(sce1) // [1 666 5] fmt.Println(sce2) // [1 666 5] }
package main import "fmt" func main() { var sce1 = []int{1, 3, 5} var sce2 = make([]int, 5) fmt.Printf("賦值前:%p\n", sce1) // 0xc0420600a0 fmt.Printf("賦值前:%p\n", sce2) // 0xc042076060 // 將sce1中的數據拷貝到sce2中,, 此時sce1和sce2底層指向不同數組 copy(sce2, sce1) fmt.Printf("賦值后:%p\n", sce1) // 0xc0420600a0 fmt.Printf("賦值后:%p\n", sce2) // 0xc042076060 //copy(sce2, sce1) fmt.Println(sce1) // [1 3 5] fmt.Println(sce2) // [1 3 5 0 0] sce2[1] = 666 fmt.Println(sce1) // [1 3 5] fmt.Println(sce2) // [1 666 5 0 0] }
- copy函數在拷貝數據時永遠以小容量為準
package main import "fmt" func main() { // 容量為3 var sce1 = []int{1, 3, 5} // 容量為5 var sce2 = make([]int, 5) fmt.Println("拷貝前:", sce2) // [0 0 0 0 0] // sce2容量足夠, 會將sce1所有內容拷貝到sce2 copy(sce2, sce1) fmt.Println("拷貝后:", sce2) // [1 3 5 0 0] }
package main import "fmt" func main() { // 容量為3 var sce1 = []int{1, 3, 5} // 容量為2 var sce2 = make([]int, 2) fmt.Println("拷貝前:", sce2) // [0 0] // sce2容量不夠, 會將sce1前2個元素拷貝到sce2中 copy(sce2, sce1) fmt.Println("拷貝后:", sce2) // [1 3] }
- 切片的基本使用方式和數組一樣, 可以通過
-
切片的注意點
- 可以通過切片再次生成新的切片, 兩個切片底層指向同一數組
package main import "fmt" func main() { arr := [5]int{1, 3, 5, 7, 9} sce1 := arr[0:4] sce2 := sce1[0:3] fmt.Println(sce1) // [1 3 5 7] fmt.Println(sce2) // [1 3 5] // 由于底層指向同一數組, 所以修改sce2會影響sce1 sce2[1] = 666 fmt.Println(sce1) // [1 666 5 7] fmt.Println(sce2) // [1 666 5] }
- 和數組不同, 切片只支持判斷是否為nil, 不支持==、!=判斷
package main import "fmt" func main() { var arr1 [3]int = [3]int{1, 3, 5} var arr2 [3]int = [3]int{1, 3, 5} var arr3 [3]int = [3]int{2, 4, 6} // 首先會判斷`數據類型`是否相同,如果相同會依次取出數組中`對應索引的元素`進行比較, // 如果所有元素都相同返回true,否則返回false fmt.Println(arr1 == arr2) // true fmt.Println(arr1 == arr3) // false sce1 := []int{1, 3, 5} sce2 := []int{1, 3, 5} //fmt.Println(sce1 == sce2) // 編譯報錯 fmt.Println(sce1 != nil) // true fmt.Println(sce2 == nil) // false }
- 只聲明當沒有被創建的切片是不能使用的
package main import "fmt" func main() { // 數組聲明后就可以直接使用, 聲明時就會開辟存儲空間 var arr [3]int arr[0] = 2 arr[1] = 4 arr[2] = 6 fmt.Println(arr) // [2 4 6] // 切片聲明后不能直接使用, 只有通過make或語法糖創建之后才會開辟空間,才能使用 var sce []int sce[0] = 2 // 編譯報錯 sce[1] = 4 sce[2] = 6 fmt.Println(sce) }
- 字符串的底層是[]byte數組, 所以字符也支持切片相關操作
package main import "fmt" func main() { str := "abcdefg" // 通過字符串生成切片 sce1 := str[3:] fmt.Println(sce1) // defg sce2 := make([]byte, 10) // 第二個參數只能是slice或者是數組 // 將字符串拷貝到切片中 copy(sce2, str) fmt.Println(sce2) //[97 98 99 100 101 102 103 0 0 0] }