複合資料類型也是由基礎數量類型構造出來的,golang中主要有數組、slice(切片)、map和結構體四種。數組和結構體都是有固定記憶體大小的資料結構。相比之下,slice和map則是動态的資料結構,它們将根據需要動态增長。
二、 golang 複合資料類型特别之處
1. 數組
數組定義定義及初始化,數組長度是固定的,且為常量,在編譯時已确定。
var q [3]int = [3]int{1, 2, 3}
q := [...]int{1, 2, 3} // 或通過...占位,自動計算個數
可通過
range
在周遊數組的索引和值。
var a [3]int
for index, value := range a {
fmt.Printf("%d %d\n", index, value)
}
// 僅擷取value
for _, value := range a {
fmt.Printf(""%d\n", value)
}
向函數傳遞數組時不允許在函數内部修改底層數組的元素。
main(){
num := [3]int{1, 2, 3}
myInt := 100
changeNum(myInt, num)
fmt.Println(num[0], myInt)
}
func changeNum(myInt int, num [3]int) {
num[0] = 0
myInt++
}
//output
1 100
我們傳入changeNum函數一個變量和數組,底層數組資料均未被修改。在golang中函數參數變量接收的是一個複制的副本,并不是原始調用的變量。如果要通過數組修改底層數組資料可以像其他語言一樣傳入數組指針。
main(){
num := [3]int{1, 2, 3}
changeNumPoint(&num)
fmt.Println(num[0])
}
func changeNumPoint(num *[3]int) {
num[0] = 0
}
//output
0
2.slice
切片,和python中切片相似度較高,slice相比數組,其長度是可擴充的,slice建立一個合适大小的數組,然後slice的指針指向底層的數組。
slice 内部結構體
struct Slice
{
byte* array; // actual data
uintgo len; // number of elements
uintgo cap; // allocated number of elements
};
第一個指針是指向底層數組,後面是slice的長度和容量。在底層,make建立了一個匿名的數組變量,然後傳回一個slice;隻有通過傳回的slice才能引用底層匿名的數組變量。我們可使用内建make建立slice。
make([]T, len, cap) //cap省缺于len值相同
slice長度: 切片包含的元素個數。
slice容量: 從它的第一個元素開始數,到其底層數組元素末尾的個數。使用append添加資料時,容量小于長度時會自動擴充為原來兩倍。
slice值包含指向第一個slice元素的指針,是以向函數傳遞slice将允許在函數内部修改底層數組的元素,上文提到數組傳入函數是以副本形式。
main(){
sliceNum := []int{4, 5, 6}
changeSlice(sliceNum)
fmt.Println(sliceNum[0]
}
func changeSlice(sliceNum []int) {
sliceNum[0] = 0
}
//output
0
使用append追加多個元素:
x = append(x, 4, 5, 6)
copy(dest,src),對位複制。
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{6, 7, 8}
copy(slice2, slice1)
3.Map
哈希表,類似于python中dic字典,map是一個無序的key/value對的集合。
通slice一樣,可以使用make建立一個map,也可以通過range周遊:
ages := make(map[string]int){"key":value}
通過key作為索引下标來通路map将産生一個value。如果key在map中是存在的,那麼将得到與key對應的value;如果key不存在,那麼将得到value對應類型的零值。我們可以通過這個值來判斷key索引的value是否存在。
age, ok := ages["key"]
if !ok { ... }
//可以優化為
if age, ok := ages["bob"]; !ok { ... }
map的比較
func equal(x, y map[string]int) bool {
if len(x) != len(y) {
return false
}
for k, xv := range x {
if yv, ok := y[k]; !ok || yv != xv {
return false
}
}
return true
}
使用map做統計
var m = make(map[string]int)
func k(list []string) string { return fmt.Sprintf("%q", list) }
func Add(list []string) { m[k(list)]++ }
func Count(list []string) int { return m[k(list)] }
4.結構體
結構體定義
type Point struct {
Salary int
ManagerID int
}
var p Point //結構體變量
//另一種寫法
type Point struct{ X, Y int }
p := Point{1, 2}
和變量、函數一樣,構體成員名字是以大寫字母開頭的,那麼該成員就是導出的。一個結構體可能同時包含導出和未導出的成員。
結構體作為函數的參數和傳回值:
func Scale(p Point, factor int) Point {
return Point{p.X * factor, p.Y * factor}
}
fmt.Println(Scale(Point{1, 2}, 5)) // "{5 10}"
指針的方式(提高效率),可以在函數中修改結構體成員。
func AwardAnnualRaise(e *Employee) {
e.Salary = e.Salary * 105 / 100
}
結構體嵌套,使定義簡化和清晰。
//點
type Point struct {
X, Y int
}
//圓
type Circle struct {
Center Point
Radius int
}
//輪
type Wheel struct {
Circle Circle
Spokes int
}
通路成員
var w Wheel
w.Circle.Center.X = 8
w.Circle.Center.Y = 8
w.Circle.Radius = 5
w.Spokes = 20
顯然要通路多層結構體較為複雜,go還提供了
匿名成員
來處理結構嵌套。匿名成員的資料類型必須是命名的類型或指向一個命名的類型的指針。
我們修改圓和輪為如下:
type Circle struct {
Point
Radius int
}
type Wheel struct {
Circle
Spokes int
}
通路成員:
var w Wheel
w.X = 8
w.Y = 8
w.Radius = 5
w.Spokes = 20
從上面來看匿名成員特性隻是對通路嵌套成員的點運算符提供了簡短的文法糖。,其實在go中匿名成員并不要求是結構體類型;其實任何命名的類型都可以作為結構體的匿名成員。這個機制可以用于将一些有簡單行為的對象組合成有複雜行為的對象。組合會在go面向對象中會大展拳腳。
文章有不足的地方歡迎在評論區指出。
歡迎收藏、點贊、提問。關注頂級飲水機管理者,除了管燒熱水,有時還做點别的。