1. 程式人生 > >Go語言程式設計——集合型別

Go語言程式設計——集合型別

一、值、指標和引用型別。

值在傳遞給函式或者方法的之後會被覆蓋一次。這對於數字型別或者布林型別來說是非常廉價的,因為這樣的位元組只佔1~8個位元組。

按值傳遞字串也是非常廉價的,因為Go語言中字串是不可變的,Go語言編譯器會將傳遞過程進行安全優化,因此無論傳遞字串長度多少,實際傳遞資料量都會非常小。64位計算機16位元組,32位機器8位元組。

Go語言陣列是按值傳遞的,因此傳遞一個數組代價非常大。幸運的是,在Go語言中陣列不長用到,我們可以使用切片來代替,傳遞一個切片的成本和字串差不多。另外,修改切片也不會導致寫時複製的負擔,因為不同於字串的是,切片是可變的。

不管指標所指向值的大小,指標的傳遞時非常廉價64的64位計算機8位元組,32位機器4位元組。同時,對於某個指標所指向的變數,只要保證至少有一個指標指向該變數,改變數就會在記憶體中儲存足夠長的時間。因此他們的生命週期獨立於作用。

指標和值
語句變數型別記憶體地址
x := 3x3

int

0xf840000148

y := 22

y

22

int

0xf840000150
x == 3 && y == 22
pi := &x

x

3int0xf840000148
pi0xf840000148*int0xf840000158
*pi ==3 && x==3 && y ==22
x++x4int0xf840000148
pi0xf840000148*int0xf840000158
*pi ==4 && x==4 && y ==22
*pi++x5int0xf840000148
pi0xf840000148*int0xf840000158
*pi == 5 && x == 5 && y == 22

除了值和指標之外,Go語言也有引用型別。一個引用型別的變數指向記憶體中某個隱藏的值,它儲存著實際的資料。儲存引用型別的變數傳遞時也非常廉價64位機器一個切片16位元組,一個對映8位元組。其使用語法與值一樣。

Go語言中有些型別是引用型別:對映、切片、通道、函式和方法。與指標不同的是,引用型別沒有特殊的語法,因為他們就像值一樣。指標也可以指向一個引用型別,雖然他只對切片有用,但有時這個用法很關鍵。

如果我們定義了一個變數來儲存一個函式,該變數得到的實際是該函式的引用。函式引用知道他們所引用的函式的簽名,因此不能傳遞一個簽名不匹配的函式引用。

二、陣列和切片

1、陣列

Go語言陣列是一個定長的序列,其中的元素型別相同。多維陣列可以簡單地使用自身為陣列的元素來建立。

陣列使用一下語法建立

[length]Type
[N]{value1,value2,...,valueN}
[...]Type{value1,valuse2,...,valueN}

如果在這種場景中使用了…(省略號)操作符,Go語言會自動計算陣列的長度。在任意情況下,陣列的長度都是固定且不可修改的。

2、切片

Go語言中的切片是長度可變、容量固定的相同型別元素序列。雖然切片的容量固定,但也可以通過將其切片或者使用內建的append()函式來增加。 雖然陣列和切片儲存的元素型別相同,但在實際使用中並不受此限制。可以因為型別可以是一個介面。因此我們可以儲存任意滿足所宣告的介面的元素。

我們可以使用一下語法來建立一個切片

make([]Type,length,capacity)
make([]Type,length)
[]Type{}
[]Type{value1,value2,value3,...,valueN}

內建的make()用於建立切片、對映和通道。 當建立一個切片時,他會建立一個隱藏的初始化為零值的陣列,然後返回一個引用該隱藏陣列的切片。該隱藏的陣列與Go語言中的所有陣列一樣,都是固定長度的,如果使用第一種語法建立,那麼其長度即為切片的容量;如果使用第二種語法建立,那麼其長度即為切片的長度;如果使用複合語法建立,那麼長度為大括號中項的個數。

一個切片的容量即為其隱藏陣列的長度,而其長度則為不超過該容量的任意值。

遍歷切片

如果我們想獲取切片中的某個元素而不想修改它,那我們可以使用for…range迴圈,如果想要修改它則可以使用帶迴圈計數器的for迴圈。

type Product struct {
	name string
	price float64
}

func (product Product)String() string  {
	return fmt.Sprintf("%s (%.2f)",product.name,product.price)
}

func main() {
	products := []*Product{{"Spa",3.99},{"Wre",3.20},{"Scr",1.99}}
	fmt.Println(products)
	for _,product := range products{
		product.price += 0.50
	}
	fmt.Println(products)
}

修改切片

如果我們想往切片中追加元素,可以使用內建的append()函式。這個函式接受一個需要被追加的切片,以及一個或者多個需要被追加的元素。如果我們希望往一個切片中追加另一個切片,那麼我們必須使用…(省略號)操作符。

	s := []string{"A","B","C","D","E","F","G"}
	t := []string{"K","L","M","N"}
	u := []string{"m","n","o","p","q","r"}
	s = append(s,"h","i","j")
	s = append(s,t...)
	s = append(s,u[2:5]...)
	b := []byte{'U','V'}
	letters := "WXY"
	b = append(b,letters...)
	fmt.Printf("%v\n%s\n",s,b)

內建的append()函式接受一個切片和一個或者更多個值,返回一個(可能為新的)切片,其中包含原始切片的內容並將給定的值作為其後續項。

如果我們想往切片的的前面或者中間插入項,下面給出解決辦法。

func InsertStringSliceCopy(slice,insertion []string,index int) []string  {
	result := make([]string,len(slice)+len(insertion))
	at := copy(result,slice[:index])
	at += copy(result[at:],insertion)
	copy(result[at:],slice[index:])
	return result
}

此函式不會改變原始切片。

如果想改變原始切片,用下方函式。

func InsertStringSlice(slice,insertion []string,index int) []string {
	return append(slice[:index],append(insertion,slice[index:]...)...)
}

如果我們想從元素中間刪除元素,可以用如下函式

func RemoveStringSliceCopy(slice []string,start,end int) []string  {
	result := make([]string,len(slice)-(end-start))
	at := copy(result,slice[:start])
	copy(result[at:],slice[end:])
	return  result
}

此函式不會改變原始切片。

如果想改變原始切片,用下方函式。

func RemoveStringSlice(slice []string,start,end int) []string {
	return append(slice[:start],slice[end:]...)
}

排序和搜尋切片

以後補充

對映

Go語言中的對映是一種內建的資料結構,儲存鍵-值對的無序集合,他的容量只受到機器記憶體的限制。

語法含義、結果
m[k] = v用鍵k來將值v賦值給對映m,如果m中v存在,則覆蓋
Delete(m,k)

將k以及相關

的值從對映m中刪除,如果k不存在,則不執行任何操作

v,found := m[k]從對映m中取出鍵k對應的值並賦給v,如果k不存在,則將對映的型別0賦給v,found賦值為false
len(m)返回對映m中的項的數目
因為對映屬於引用型別,所以不管一個對映儲存了多少資料,傳遞都是很廉價的。

對映的建立方式如下。

make(map[KeyType]ValueType,initialCapacity)
make(map[KeyType]ValueType)
map[keyType]ValueType{}
map[keyType]ValueType{key:1,key2:2,...}