天天看點

Go語言快速入門(1)--複合資料類型(1) - 數組、slice數組slice

數組

數組的簡單使用

var a [3]int = [3]int{1, 2, 3}
	var b [2]int = [2]int{1, 2}
	fmt.Println(a[0])

	//輸出數組的值
	for i, j := range a {
		fmt.Printf("%d %d\n", i , j);
	}

	//僅輸出元素
	for _, v := range a {
		fmt.Printf("%d", v)
	}
           

對于數組有以下注意的點:

  • 數組長度是數組類型的一部分,不同長度的類型不一樣
package main

import "fmt"

func main() {
	var a [3]int = [3]int{1, 2, 3}
	var b [2]int = [2]int{1, 2}

	//輸出類型
	fmt.Printf("type of a: %T\n",a)
	fmt.Printf("type of b: %T\n",b)

}
//輸出
type of a: [3]int
type of b: [2]int
           
  • 如果一個元素類型是可以比較的,那麼這個數組也是可以比較的,也就是說可以直接使用“==’'進行比較
var a [3]int = [3]int{1, 2, 3}
var b [3]int = [3]int{1, 2, 3}

fmt.Println(a == b)
//輸出
true
           
  • 對數組的修改是值傳遞的,如果要進行對數組的修改,可以使用指針
package main

import "fmt"

func set(nums [3]int){
	nums[2] = 100
}
//值傳遞
func print(nums [3]int){
	for _, v := range nums {
		fmt.Printf("%d\t", v)
	}
	fmt.Println()
}

func main() {
	var a [3]int = [3]int{1, 2, 3}
	print(a)
	set(a)
	print(a)
}
           

引用傳遞的例子

func set(nums *[3]int){
	nums[2] = 100
}

func print(nums [3]int){
	for _, v := range nums {
		fmt.Printf("%d\t", v)
	}
	fmt.Println()
}

func main() {
	var a [3]int = [3]int{1, 2, 3}
	print(a)
	set(&a)
	print(a)
}
           

slice

數組在Go語言中并沒有那麼常用,更常用的資料結構是切片,也就是動态數組slice,使用slice可以向slice中追加元素,在容量不足的時候會進行擴容

slice是一種輕量型的資料結構,可以用來通路數組的部分或者全部元素,slice有三個屬性:指針、長度、容量。

指針

指向數組中的第一個可以從slice中通路的元素,這個元素并不一定是數組的第一個元素

長度

是指slice中的元素個數,它不能超過slice容量

容量

通常是從slice的起始元素到底層數組的最後一個元素間的個數

slice包含了指向數組元素的指針,也就是說将一個slice傳遞給一個函數的時候,是引用傳遞的,函數内部可以修改底層數組的元素,例如reverse反轉函數

//反轉
func reverse(s []int) {
	for i, j := 0, len(s) - 1; i < j; i, j = i + 1, j - 1 {
		s[i], s[j] = s[j], s[i]
	}
}
func main() {
	a := []int{1, 2, 3, 4, 5}
	reverse(a)
	fmt.Println(a)
}
//輸出
[5 4 3 2 1]
           

将一個左移n個元素的做法是連續調用reverse函數三次,第一次反轉前n個元素,第二次反轉剩下的元素,最後對一整個slice進行反轉

//反轉
func reverse(s []int) {
	for i, j := 0, len(s) - 1; i < j; i, j = i + 1, j - 1 {
		s[i], s[j] = s[j], s[i]
	}
}
func main() {
	a := []int{1, 2, 3, 4, 5}
	//向左移動兩個元素 [3, 4, 5, 2, 1]
	reverse(a[:2])
	reverse(a[2:])
	reverse(a)
	//reverse(a)
	fmt.Println(a)
}
           

數組的類型如果相同是可以進行比較的,但是slice是不能比較的,是以不能使用==測試兩個slice是否具有相同的元素,主要有兩個原因:

  • slice的元素是非直接的,有可能slice可以包含本身
  • slice的元素不是直接的,是以如果底層數組元素發生改變,同一個slice在不同的時間内會具有不同的元素

slice可以和nil進行比較,值為nil的slice沒有對應的底層數組,值為nil的slice長度和容量都為0,但是也有非nil的slice長度和容量是0

var s []int //len(s) == 0 s == nil
s = nil 	//len(s) == 0 s == nil
s = []int(nil) 	//len(s) == 0, s == nil
s = []int{}		//len(s) == 0, s != nil
           

是以,檢查一個slice是否為空需要使用len(s) == 0,而不是s == nil,因為s != nil的情況下,slice也有可能為空

append 函數

内置函數append可以将元素追加到slice的後面,下面通過一個自己實作的appendInt方法來了解内置函數append

func appendInt(x []int, y int) []int {
	var z[] int
	zlen := len(x) + 1
	if zlen < cap(x) {
		z = x[:zlen]
	}else {
		zcap := zlen
		if zcap < 2 * len(x) {
			zcap = 2 * len(x)
		}
		z = make([]int, zlen, zcap)
		copy(z, x)

	}
	z[len(x)] = y
	return z
}
func main() {
	var x, y []int
	for i := 0;i < 10; i++ {
		y = appendInt(x, i)
		fmt.Printf("%d cap = %d\t%v\n", i, cap(y), y)
		x = y
	}
}
//效果
1 cap = 2       [0 1]
2 cap = 4       [0 1 2]
3 cap = 6       [0 1 2 3]
4 cap = 6       [0 1 2 3 4]
5 cap = 10      [0 1 2 3 4 5]
6 cap = 10      [0 1 2 3 4 5 6]
7 cap = 10      [0 1 2 3 4 5 6 7]
8 cap = 10      [0 1 2 3 4 5 6 7 8]
9 cap = 18      [0 1 2 3 4 5 6 7 8 9]
           

每一次的appendInt的調用都必須檢查slice是否有足夠的容量來存儲新的元素,如果slice容量足夠,那麼就會定義一個新的slice,但是該slice仍然引用原始底層數組,然後将新元素y複制到新的位置,并傳回這個slice,也就是說輸入參數slice和函數的傳回值slice z具有相同的傳回值

如果slice的容量不過容納增長的元素,appendInt函數必須建立一個新的具有足夠容量的新的底層數組來存儲新的元素,然後将slice x複制到這個數組,再将新元素y追加到數組後面,傳回值slice z和輸入參數slice

Go語言快速入門(1)--複合資料類型(1) - 數組、slice數組slice
Go語言快速入門(1)--複合資料類型(1) - 數組、slice數組slice

再通過一個例子了解append之後的slice底層數組是否改變的問題

func main() {
	s := []int{5}
	fmt.Println("cap(s) = ", cap(s), "ptr(s) = ", &s[0])


	s = append(s, 7)
	fmt.Println("cap(s) = ", cap(s), "ptr(s) = ", &s[0])

	s = append(s, 9)
	fmt.Println("cap(s) = ", cap(s), "ptr(s) = ", &s[0])

	x := append(s, 11)
	fmt.Print("cap(s) = ", cap(s), " ptr(s) = ", &s[0], " ptr(x) =", &x[0])
	fmt.Println(" s中的元素: ", s, "x中的元素: ", x)

	y := append(s, 12)
	fmt.Print("cap(s) = ", cap(s), " ptr(s) = ", &s[0], " ptr(y) =", &y[0])
	fmt.Println(" s中的元素: ", s, "y中的元素: ", x)

}
           
Go語言快速入門(1)--複合資料類型(1) - 數組、slice數組slice
  • 建立s時,cap(s) == 1現在記憶體中的資料隻有[5]
  • s = append(s, 7),按照slice的擴容機制,cap(s)翻倍,這個時候記憶體中的資料[5,7],建立了一個新的底層數組
  • s = append(s, 9),同樣的按照slice的擴容機制,cap(s)翻倍,記憶體中的資料為[5,7,9]
  • x := append(s, 11):不需要擴容,x記憶體中的資料為[5,7,9,11]但是s的資料為[5,7,9],

    需要注意的事情是slice x和slice s指向的底層數組是同一個數組

) == 1現在記憶體中的資料隻有[5]

  • s = append(s, 7),按照slice的擴容機制,cap(s)翻倍,這個時候記憶體中的資料[5,7],建立了一個新的底層數組
  • s = append(s, 9),同樣的按照slice的擴容機制,cap(s)翻倍,記憶體中的資料為[5,7,9]
  • x := append(s, 11):不需要擴容,x記憶體中的資料為[5,7,9,11]但是s的資料為[5,7,9],

    需要注意的事情是slice x和slice s指向的底層數組是同一個數組

  • y := append(s, 12):與上面同理

繼續閱讀