Go(Golang) 是一個開源的編程語言,它能讓構造簡單、可靠且高效的軟體變得容易,
Go是從2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持開發,后來還加入了Ian Lance Taylor, Russ Cox等人,并最終于2009年11月開源,在2012年早些時候發布了Go 1穩定版本,
現在Go的開發已經是完全開放的,并且擁有一個活躍的社區,
在國內外,已經有很多大廠開始大規模使用Golang開發其云計算相關產品,比如Google、AWS、Cloudflare、阿里巴巴等,
而Go開發人員的全球平均薪資也是相當高,在美國,使用Go語言的開發者平均年薪為$ 136K,
在2019年Stack Overflow開發者調查中,Go是全球收入第三的語言,
因此,還沒有開始學習Go的朋友,不妨來看看我今天推薦的教程《Go 語言實作常見資料結構》,
通過使用 Golang 實作常見的資料結構,加深大家對 Golang 的理解,并且可以強化大家的資料結構基本功,
先讓我們一起來看看《Go 語言實作常見資料結構》節選吧,本節教程主要教大家學習Golang的陣列與切片,
大家也可以點擊鏈接,在實驗樓搭載好的線上環境,跟著教程學習,效率更高,
教程節選:
實驗1 陣列與切片
實驗介紹
從本實作開始我們將進入到 Golang 的學習之旅,Golang 的許多初學者都會對陣列 (Array) 與切片 (Slice) 感到困惑,
他們雖然同屬于集合類的型別,但是用起來卻十分不同,在本節實驗中,你將學習到陣列與切片到底是哪里不同,這里也是 Golang 面試中的一個常考知識點,
知識點
- 陣列的資料型別
- 陣列的創建
- 陣列的遍歷
- Golang 陣列與切片的區別
- 切片的擴容規律
Golang 陣列基本操作
這一節開始,我們將學習 Golang 陣列與切片的常用方法以及他們在具體面試中的常考知識點,
陣列的宣告
Golang 中一個陣列的宣告方式主要有以下幾種,
package main
func main() {
// 第一種,在初始化時只宣告陣列長度,不宣告陣列內容
var arr1 [5]int
// 第二種,知道資料很多,不想自己寫長度的時候可以用這種方式
// 宣告之后由編譯器自己推算陣列長度
arr2 := [...]int{1,3,5,7,9}
// 第三種,宣告的時候長度和初值一起宣告
arr3 := [3]int{2,4,6}
// 二維陣列的宣告,其意義是三行五列
var Block [3][5]int
}
這里值得一提的是 Golang 中的陣列的初始值如果你不做宣告的話默認是全部有初值的, 比如 arr1 這個陣列雖然只宣告了長度為 5,但是 Go 的編譯器也會把這 5 個元素全都初始化為 0,而對于 bool 值型別的陣列,如果不做賦值操作,則初始值全為 false,在接下來的陣列遍歷中我們會實際的驗證它,
陣列的遍歷
我們先在實驗樓在線實驗環境中新建新建一個名叫 array 的檔案夾,如下圖所示,先點擊 File,然后點擊 New Folder 創建名為 array 的檔案夾,
然后我們右鍵點擊 array 檔案夾,選擇 New File,創建一個叫 main.go 的檔案,如下圖所示,
Go 的陣列遍歷主要有以下兩種方式,首先鍵入以下代碼:
package main
import "fmt"
func main() {
// 第一種,在初始化時只宣告陣列長度,不宣告陣列內容
var arr1 [5]int
// 第二種,知道資料很多,不想自己寫長度的時候可以用這種方式
// 宣告之后由編譯器自己推算陣列長度
//arr2 := [...]int{1,3,5,7,9}
//// 第三種,宣告的時候長度和初值一起宣告
//arr3 := [3]int{2,4,6}
//// 二維陣列的宣告,其意義是三行五列
var Block [3][5]bool
// 第一種
for i := 0 ; i<len(arr1); i++{
fmt.Printf("%d\n",arr1[i])
}
// 第二種
for index, value := range arr1 {
fmt.Printf("索引:%d, 值: %d\n",index,value)
}
// 以第二種方式遍歷二維陣列,只取值,也就是取出一個陣列
for _,v := range Block {
// 再對這個陣列取值
for _,value := range v {
fmt.Printf("%v ",value)
}
fmt.Printf("\n")
}
}
接下來,在終端執行:
cd array
go run main.go
結果如下:
其中 Go 語言官方更加提倡的是第二種以 range 的方式進行遍歷,這樣寫會讓代碼更加優雅,而且絕對不會越界,
那么,如果我只想要陣列里的 index 不想要 value 時怎么 range 呢?
答案其實很簡單,i := range arr 就可以了,如果你只想要 value 不想要索引的時候就可以這樣寫 _, value := range arr, 注意這里的下劃線不能省略,
封裝一個陣列列印函式
現在咱們封裝一個用來列印陣列的函式并對其進行測驗,代碼如下:
func PrintArr(arr [5]int) {
// 第二種
for index, value := range arr {
fmt.Printf("索引:%d, 值: %d\n",index,value)
}
}
我們分別將 arr1,2,3 傳入列印,先猜測一下會發生什么結果呢?
package main
import "fmt"
func main() {
// 第一種,在初始化時只宣告陣列長度,不宣告陣列內容
var arr1 [5]int
// 第二種,知道資料很多,不想自己寫長度的時候可以用這種方式
// 宣告之后由編譯器自己推算陣列長度
arr2 := [...]int{1,3,5,7,9}
//// 第三種,宣告的時候長度和初值一起宣告
arr3 := [3]int{2,4,6}
PrintArr(arr1)
PrintArr(arr2)
PrintArr(arr3)
}
func PrintArr(arr [5]int) {
// 第二種
for index, value := range arr {
fmt.Printf("索引:%d, 值: %d\n",index,value)
}
}
結果是程式在列印 arr3 時拋出了如下例外,
這個就要牽扯出一個概念了,Go 語言中陣列是值型別,也就是說[3]int,和[5]int 在 go 中會認為是兩個不同的資料型別,
同樣地,你在 PrintArr 中改變陣列中的值也不會改變原陣列的值,
到了這里你肯定覺得 go 的陣列太難用了,又要資料型別統一又要長度統一才能傳遞,確實是這樣的,在 go 中我們一般不直接使用陣列,而是使用我們今天的主角,切片,
Golang 切片的基本操作
一般而言,Go 語言的切片比陣列更加靈活,強大而且方便,陣列是按值傳遞的(即是傳遞的副本),而切片是參考型別,傳遞切片的成本非常小,而且是不定長的,
而且陣列是定長的,而切片可以調整長度,創建切片的語法如下:
- make([ ]Type, length, capacity)
- make([ ]Type, length)
- [ ]Type{}
- [ ]Type{value1, value2, ..., valueN}
內置函式 make() 用于創建切片、映射和通道,當用于創建一個切片時,它會創建一個隱藏的初始化為零值的陣列,然后回傳一個參考該隱藏陣列的切片,
該隱藏的陣列與 Go 語言中的所有陣列一樣,都是固定長度,如果使用第一種語法創建,那么其長度為切片的容量 capacity ;如果是第二種語法,那么其長度記為切片的長度 length ,一個切片的容量即為隱藏陣列的長度,而其長度則為不超過該容量的任意值,另外可以通過內置的函式 append() 來增加切片的容量,
我們來執行一下下面的程式:
package main
import "fmt"
func main() {
slice := make([]int,0)
for i := 0 ;i < 10; i++ {
// 動態的對切片進行擴容
slice = append(slice, i)
}
fmt.Println(slice)
// 呼叫PrintArr
PrintArr(slice)
// 看看是否切片的第一個元素改變了?
fmt.Println(slice)
}
func PrintArr(arr []int) {
arr[0] = 100
for index, value := range arr {
fmt.Printf("索引:%d, 值: %d\n",index,value)
}
}
執行結果:
執行之后我們會發現 slice[0] 的值確實被改變了,因為切片是參考傳遞,也就是直接把切片在記憶體中的地址傳遞過去,
這樣我們在其他函式中對其進行修改也會影響原來的切片的資料,這樣做的好處是傳參考因為不用把原資料拷貝一份,所以對系統的開銷比較小,
切片的切割
切片最大的特色就是可以靈活的進行切分,比如下面的例子,
package main
import "fmt"
func main() {
slice := make([]int,0)
for i := 0 ;i < 10; i++ {
slice = append(slice, i)
}
fmt.Println(slice)
s2 := slice[2:4]
fmt.Println(s2)
}
執行結果:
這里的 2 可被稱為起始索引,4 可被稱為結束索引,那么 s2 的長度就是 4 減去 2,即 2,因此可以說,s2 中的索引從 0 到 1 指向的元素對應的是 slice 及其底層陣列中索引從 2 到 3 的那 2 個元素,
到這里我們就可以推出 [n:m] 的意思是取區間 [n,m) 的資料賦值給新的切片,
關于陣列與切片兩道常見面試題
- Golang 切片的擴容規則,
一旦一個切片無法容納更多的元素,Go 語言就會想辦法擴容,但它并不會改變原來的切片,而是會生成一個容量更大的切片,然后將把原有的元素和新元素一并拷貝到新切片中,在一般的情況下,你可以簡單地認為新切片的容量將會是原切片容量的 2 倍, 但是,當原切片的長度大于或等于 1024 時,Go 語言將會以原容量的 1.25 倍作為新容量的基準,因為繼續再乘以 2 的話切片容量增加的太快,很容易產生大量的浪費無意義的空間, 不過,如果我們一次追加的元素過多,以至于使新長度比原容量的 2 倍還要大,那么新容量就會以新長度為基準,比如你現在的切片長度為 10,現在一下往里面添加了 30 個元素,那么 Golang 會直接創建一個新的長度為 40 的底層陣列,然后把所有的資料拷貝進去,
- Golang 切片的底層陣列在什么情況下會改變?
其實這里的典型回答應該是永遠不會改變,因為當切片需要擴容時,新的切片誕生的同時也會創建出新的底層陣列,它只是把原陣列的資料拷貝了進來,并未對其做任何的修改,
一道思考題
現在我們已經學習了如何對陣列進行“擴容”,那么你能否使用“擴容”的方式,把原切片進行縮容呢?請嘗試寫出代碼,或查閱相關資料,
......
篇幅有限,暫時發布以上內容,
之后還有“堆疊與堆疊的應用”“佇列與回圈佇列”等內容可以學習,感興趣的朋友可以點擊鏈接,學習之后的內容,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/30706.html
標籤:Go
上一篇:堆疊-鏈式存盤
