1. 程式人生 > >(三)go的高階資料與結構型別

(三)go的高階資料與結構型別

1.go的陣列與切片

package main

import "fmt"

/*
陣列是go語言程式設計中最常用的結構之一。顧名思義,陣列就是一系列同一型別資料的集合。
陣列中包含的每一個元素成為陣列元素(element),一個數組包含的元素的個數被稱為陣列長度
 */
func main(){
	var arr1 [5]int //宣告一個數組arr1,裡面可以存五個int型別的資料
	fmt.Println(arr1) //[0 0 0 0 0],沒有初始化,go語言也不會報錯,會預設初始化為零值
	//還可以修改
	arr1[1] = 100 //修改陣列會直接在陣列本身上修改,但是不可以越界,不可以arr[7] = 100
	fmt.Println(arr1) //[0 100 0 0 0]

	var arr2 = [5]int{1,2,3} //初始化只給三個值,那麼剩餘的會自動用零值填充
	fmt.Println(arr2) //1 2 3 0 0]

	//如果我們不確定有多少個元素,或者我們不想數,該怎麼辦呢?
	var arr3 = [...]int{15,112,4,2} //使用...表示讓編譯器幫我們數有多少個元素,而且我們輸入幾個元素,就代表陣列的長度是多少
	fmt.Println(arr3) //[15 112 4 2]

	//我們在初始化陣列的時候,還可以指定某幾個元素的值
	//比方說我初始化長度為10的陣列,但指定索引為2的值是10,索引為8的元素是25,怎麼做呢?
	var arr4 = [10]int{2:10,8:25}
	fmt.Println(arr4) //[0 0 10 0 0 0 0 0 25 0]
	//如果我這裡沒有指定陣列長度,而是用...代替的話,那麼arr4的長度就為9
	//因為我們這裡指定索引為8的元素是25,那麼編譯器就將陣列的最大索引定位8,所以是9個元素
	//如果我們初始化 var arr4 = [...]{100: 20}, 那麼長度就是101
	//因為我們既然指定了索引為100的值是20,那麼陣列至少有101個,而我們又是...,那麼go編譯器就直接預設生成長度為101的陣列
}
package main

import "fmt"

/*
在go中,切片是長度可變,容量固定的相同元素的數列。
go切片本質上是一個數組,容量固定是因為陣列的容量是固定的,切片的容量就是隱藏陣列的長度,切片的長度可變是指在陣列的長度範圍內可變
怎麼理解呢?切片可以看做指向陣列的指標,切片具有長度和容量
長度:切片元素的個數
容量:切片底層陣列的個數

切片的定義方式和陣列類似,不同的是[]裡面不需要任何東西
 */
func main() {
	var slice1= []int{1, 2, 3, 4, 5, 6}
	//len表示切片的長度,cap表示切片的容量也就是底層陣列的長度
	//不過這裡貌似沒有底層陣列,就把[6]int{1,2,3,4,5,6]看成底層陣列就可以了
	fmt.Println(len(slice1), cap(slice1)) // 6 6 ,此時切片的長度和容量都是6

	//我們初始化一個數組
	var arr= [...]int{1, 2, 3, 4, 5, 6}
	var slice2= arr[:]                   //此時的arr就是slice2的底層陣列,可以把slice2看成是arr的view,通過切片來看底層的陣列
	fmt.Println(len(slice2), cap(slice2)) // 6 6
	//我們修改一下slice2
	slice2[1] = 100
	fmt.Println(arr) //[1 100 3 4 5 6]
	//當我們修改了slice2,發現arr的值也被改變了,說明切片是一個指向陣列的指標,修改切片等於修改陣列

	//我們再來看一個有趣的例子
	s1 := arr[1:3]
	fmt.Println(s1) //[100 3]
	//這裡的s1裡面只有兩個元素,但是我們取了1: 4
	s2 := s1[1:4]
	fmt.Println(s2) //[3 4 5],這裡列印了3, 4, 5
	//為什麼會發生這種現象
	/*
	arr: 	1 100 3 4 5 6
	s1:   	  100 3
	s2:           3 4 5
	上面說了,切片是底層陣列的一個view,s1和s2都是view這個arr,是可以擴充套件的
	可以向後看,但是不可以向前看,s2向前最多也就只能看到100,不可能看到100前面的1,
	如果指定s1[1:8]那麼顯然就不行了,因為底層陣列只有6個元素,索引最大的才是5,而且s1[0]相當於arr[1],所以s1最多就是s1[:5]
	 */

	var arr2 = [5]int{1,2,3,4,5}
	var s3 = arr2[:3]
	fmt.Println(s3) //[1 2 3]
	//同理我們這裡列印s3[0: 5]也沒有問題,因為s3view底層陣列arr2,但如果是s3[4]就會越界,因為切片沒有索引為4的元素
	fmt.Println(s3[0: 5]) //[1 2 3 4 5]
	//之前不說數切片的長度在陣列長度的範圍內是可變的嗎?怎麼變呢?可以使用append
	s3 = append(s3, 500)//我往s3的末尾新增一個500
	fmt.Println(s3) //[1 2 3 500]
	fmt.Println(arr2) // [1 2 3 500 5]
	//首先s3很好理解,但是arr2為什麼變了。
	//我們往s3的末尾新增一個500,s3又view這個arr2,等價於s3=arr2[:4],s3[3]=500
	//而且append會有一個返回值,而且可以一次性新增多個元素,append(s, element1, element2......)

	//如果我想把一個切片裡的元素全部新增進去該怎麼辦?在python中可以使用*,在go中可以使用...
	append_slice := []int{100,200}
	s3 = append(s3, append_slice...)
	fmt.Println(s3) //[1 2 3 500 100 200],此時我們動態新增已經超過了底層陣列的容量
	//我們來看看底層陣列
	fmt.Println(arr2) //[1 2 3 500 5]
	//底層陣列沒有變化,因為我們一次性新增兩個元素,已經超過了底層陣列的長度。
	//此時go會自動分配一個更大的陣列,讓s3去view,換句話說此時的s3已經不再view arr2,至於s3view的陣列在什麼地方,這個就不得而知了。
	//此時再改變s3
	s3[0] = 123456
	fmt.Println(arr2) //[1 2 3 500 5]
	//會發現arr2依舊沒有改變,因為s3已經不再看arr2這個陣列了,因此s3改變只會影響那個不知道在什麼地方的陣列,不會影響arr2

	//建立切片,還有另一種方式,使用make([]type, len, cap)
	s4 := make([]int, 4) //如果不寫cap,那麼預設和len一致
	fmt.Println(s4) // [0 0 0 0]
	fmt.Println(len(s4),cap(s4)) //4 4
	s4 = append(s4, 1, 2)
	fmt.Println(len(s4),cap(s4)) //6 8
	s4 = append(s4,3,4)
	fmt.Println(len(s4),cap(s4)) //8 8
	s4 = append(s4, 4)
	fmt.Println(len(s4),cap(s4)) //9 16
	//可以看到一旦滿了,就會擴大一倍


	//那麼如何遍歷陣列或者切片呢?使用for迴圈
	girls := []string{"古明地覺", "霧雨魔理沙", "芙蘭朵露斯卡雷特"}
	for i:=0;i<len(girls);i++{
		fmt.Println(girls[i])
		/*
		古明地覺
		霧雨魔理沙
		芙蘭朵露斯卡雷特
		 */
	}

	for _,name := range girls{
		fmt.Println(name)
		/*
		古明地覺
		霧雨魔理沙
		芙蘭朵露斯卡雷特
		 */
	}

}