go資料型別
go語言資料型別主要分為以下的四個大類:
- 基礎型別(整數,浮點數,負數,布林值等)
- 聚合型別(陣列,結構體)
- 引用型別(slice,指標,map,函式,通道)
- 介面型別
go語言是擁有型別系統的語言,相對於筆者最熟悉的javascript這種動態且無型別的語言來說有著長遠的好處.通過型別系統能在編譯階段減少一定的執行時錯誤.例如在go語言中不同型別之間必須通過顯示轉換來進行賦值等操作.本文主要從go語言中的基礎型別開始,逐步的講解go語言中幾種基本的引用型別.
基礎型別
字串
字串是不可改變的位元組序列.可以通過[i:j]操作符擷取對應字串的子串.由於字串不可改變的特點,子串和母串共用一端底層記憶體.
s := "hello world"
b := s[6:] // [i:j] 從i開始不包括j world
複製程式碼
常量
常量是一種表示式,可以在編譯的階段來確定相應的值.在宣告常量的時候可以指定型別和值(如果沒有指定型別會通過值來推斷常量的型別).在連續宣告多個常量的時候,主要有以下兩種方式:
const (
a = iota
b
c
d
)
// a b c d 0 1 2 3
const (
a = 1
b
c = 2
d
)
// a b c d 1 1 2 2
複製程式碼
無型別常量(常量字面量)是還沒有確定從屬型別的常量值.無型別常量相對於同樣的有型別的常量有更大的精度.例如0.0相對有浮點數擁有更大的精度.在將無型別常量複製給對應的變數的時候,賦值的變數會轉換為無型別常量預設的型別.
i := 0 // int(0)
b := 0.0 // float64(0.0)
複製程式碼
陣列
陣列是具有固定長度的儲存相同資料型別的序列.由於陣列在宣告的時候對長度有限制,當儲存元素到達陣列的容量的時候就需要申請新的記憶體空間來進行資料的儲存.所以在儲存資料上一般不會使用陣列.陣列元素的初始值是該型別的零值.在宣告陣列的時候需要顯示的指定長度和型別.
陣列操作
可以通過下面這兩種方式宣告一個陣列.
test := [...]int{1,2,3}
test := [3]int{1,2,3}
a := [2]int{} // 定義長度為2的陣列
a[0] // 0 通過下標訪問
b := [2]int{}
a == b // true
c := [3]int{}
a == c // false 這行程式碼在編譯的時候會報錯,因為長度也屬於陣列型別的一部分,長度2和長度3的陣列無法比較
array1 = [3]int{1,2,3}
array2 = [3]int{}
array2 = array1 // 同型別的陣列可以複製,對於指標陣列只複製指向.
複製程式碼
注意點
go語言在傳遞函式的引數的時候,總是以值的方式進行傳遞,對於長陣列可以考慮通過傳遞指標的方式來降低複製的效能
slice
slice是用相同型別元素的可變長度序列.可以基於一個已有的陣列來建立這個陣列的slice.slice有三個屬性:指標,容量,長度.可以在一個陣列的基礎上產生多個slice,它們共享記憶體空間.需要注意的是slice可以理解為對原陣列的引用,通過對slice的修改是會影響到底層陣列的.
宣告和使用
宣告切片
主要可以通過切片字面量和內建的make函式來實現切片的宣告.
var test = make(int[], 3, 5) // make(type[], len, cap) 宣告一個長度為3容量為5的切片
var test1 = []int{1,2} // 宣告一個長度和容量都為2的切片
var test2 = []int{ 99: 1 } // 宣告一個長度和容量為100的切片,初始化第100的元素為1
//宣告陣列與宣告切片的對比 注意宣告slice的時候不需要指定長度
test3 := [2]int{1,2}
test4 := []int{1,2}
複製程式碼
操作切片
var num = [10]int{1,2,3,4,5,6,7,8,9,10}
a := num[1:4] // 操作符[i:j]建立一個新的slice,引用原陣列i到j-1個元素.slice的容量是slice起始元素到底層陣列最後一個元素之間的個數,切片a只能看到底層陣列i以及之後的元素
len(a) // 3 獲取切片的長度
cap(a) // 9 獲取切片的容量
a[0] = 100
b := a[:5] // 可以在一個已有的slice上擴充容量,產生新的slice
len(b) // 4
cap(b) // 9
// 通過切片建立切片
slice := []int{1,2,3,4,5}
newSlice := slice[1:3]
複製程式碼
在上面最後的一個例子中,可以通過切片建立切片,slice能看到底層陣列的所有元素,newSlice只能看到底層陣列1(包括)之後的元素.
切片增長
可以通過內建的append函式來動態的增加元素到切片中.
slice := []int{1,2,3,4,5}// 宣告一個切片
newSlice := slice[1:2] // 在已有切片上建立一個切片 切片的長度是1 容量是4
newSlice = append(newSlice, 10)
fmt.Println(slice[2]) // 10
複製程式碼
在剛才的例子中,由於newSlice的容量是4,在進行append的時候沒有達到容量的限制,所以底層陣列的元素被修改了.對於這種情況在建立切片刀時候就限制切片的容量和長度一致.這樣當進行元素的新增的時候,append函式會新建立底層資料.
slice := []int{1,2,3,4,5}// 宣告一個切片
newSlice := slice[1:2:2] // 在已有切片上建立一個切片 操作符[i:j:k] 長度 j-i 容量 k-i
newSlice = append(newSlice, 10)
fmt.Println(slice[2]) // 3
複製程式碼
append還支援批量的增加元素.
a := []int{1,2}
b := []int{3,4}
c := append(a, b...)
// 迭代切片
for _, value := range c {
fmt.Println(value)
}
複製程式碼
map
在go中map是對雜湊表的引用.雜湊表是無序的鍵值的結合.可以通過如下的方式建立map:
var test = make(map[string]int) // 宣告map的鍵值的型別 map的值也可以是複合型別 例如 make(map[string]map[string]int)
test["name"] = 100 // 賦值
var person = map[string]int{ "card": 1, id: "2" } //宣告並初始map
person["card"] // 1
delete(person, "card") // 刪除對應的屬性
if value, ok := test["haha"]; !ok {
// 不存在對應的鍵值
}
複製程式碼
可以宣告一個nil對映,nil對映不能用於儲存建值.
var a map[string]int
a["hahh"] = 1 // 報錯
複製程式碼
map有以下幾點需要特別注意:
- 在賦值map型別的值的時候,需要對map進行初始化.初始化的map的值是對應型別的零值.
- 獲取鍵值的操作返回值,來判斷對應元素是否存在map中.
- map的鍵值是可以使用==來比較的(slice不可以).
- map是引用型別,在函式間傳遞的時候會對原值進行修改.
結構體
結構體是將多個命名變數組合到一起的聚合資料型別.可以通過下面的方式宣告一個結構體:
type Person struct {
name string
id,age int
baby []int
}
var haha Person
// 下面這幾種對結構體變數賦值的方式是等同的
haha.name = "haha"
test := Person{ name: "100" } // 指定變數的值的方式初始化變數
name := &haha.name
*name = "100"
var copyPerson *Person = &haha
copyPerson.name = "100"
複製程式碼
在宣告結構體的時候需要注意以下的幾點:
- 結構體變數的大小寫決定變數是否可以匯出(可以被其他匯出的包讀寫)
- 結構體變數不可以擁有自己本身的結構體型別,可以通過自身結構體型別的指標來實現遞迴結構
- 如果結構體所有的成員變數是可比較的,那麼這個結構體是可比較的
結構體巢狀
在定義結構體的成員的時候,go允許只指定成員的型別來實現成員的宣告.通過這種方式定義的結構體成員成為匿名成員.匿名成員的型別必須是一種命名型別或者指向命名型別的指標.匿名成員可以為方便變數提供便捷的操作.
type Circle struct {
x int
y int
radius int
}
type Wheel struct {
Circle
color string
}
wheel := Wheel{Circle{ 10, 10, 19}, "red"}
// wheel.x = 10
複製程式碼
後記
我自己在寫一個公眾號-前端小板凳,主要分享我學習前端和工作中的一些想法,歡迎大家關注.