天天看點

go 語言學習(5)--數組與切片

文章目錄

      • 數組的聲明
      • 數組的周遊
      • 值傳遞 or 引用傳遞
      • 切片slice
      • append
        • 擴容的規律
        • 删除數組元素
      • 總結

數組的聲明

func array() {
	var arr1 [5]int
	arr2 := [3]int{1, 3, 5}
	// 自行推斷數組長度
	arr3 := [...]int{1, 3, 5, 7, 9}
	// 二維數組
	var grid [4][5]int
	fmt.Println(arr1, arr2, arr3, grid)
	// [0 0 0 0 0] [1 3 5] [1 3 5 7 9] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
}
           

變量的聲明已經講過啦,不熟悉的可以看第二章

數組的周遊

func arrRange() {
	arr1 := [...]int{2, 4, 6, 8, 10}
	for i := 0; i < len(arr1); i++ {
		fmt.Println(arr1[i])
	}

	for _, value := range arr1 {
		fmt.Println(value)
	}

           

兩種方法,一種傳統的下标周遊一種上一章講到的

range

推薦周遊用 range, 很友善

在idea裡,傳統的

for

循環智能提示是

for

,

forr

就可以自動補全

range

值傳遞 or 引用傳遞

上一章講到這個概念了,這一章繼續舉個例子

func printArray(arr [5]int) {
	arr[0] = 100
	for _, v := range arr {
		println(v)
	}
}

func main() {
	arr1 := [5]int{1, 2, 3, 4, 5}
	arr2 := [3]int{1, 2, 3}
	printArray(arr1)
	printArray(arr2)
}
           

這個結果是什麼呢

# command-line-arguments
lesson4(array)/array.go:45:12: cannot use arr2 (type [3]int) as type [5]int in argument to printArray
           

說的是類型不比對,我需要一個

[5]int

的,你給我傳的是

[3]int

的,當然報錯啦

func printArray(arr [5]int) {
	arr[0] = 100
	for _, v := range arr {
		println(v)
	}
}
func main() {
	arr1 := [5]int{1, 2, 3, 4, 5}
	printArray(arr1)
	fmt.Println(arr1)
}
           

列印的結果是什麼?

當然還是[1,2,3,4,5]啦,因為你是把 arr1拷貝了一份,函數體内的變化并不影響外部的變量

如果想改變 arr[0]的值你得這樣

func printArray2(arr *[5]int) {
	arr[0] = 100
	for _, v := range arr {
		println(v)
	}
}

func main() {
	arr1 := [5]int{1, 2, 3, 4, 5}
	printArray(arr1)
	printArray2(&arr1)
	fmt.Println(arr1)
}
           

傳一個指向 arr1的指針,懂了嗎,在

go

裡隻有值傳遞,而且

array

這種類型也是值,而在其他語言裡比如

python

,

javascript

array

都是引用類型

這幾段代碼可能讓你覺得

go

裡的數組很難用,其實不然,下面我們講下數組的切片

切片slice

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
	fmt.Println("arr[2:6] = ", arr[2:6])
	fmt.Println("arr[2:6] = ", arr[:6])
	fmt.Println("arr[2:6] = ", arr[2:])
	fmt.Println("arr[2:6] = ", arr[:])
}
           

有程式設計基礎的應該都能知道,結果是:

arr[2:6] =  [2 3 4 5]
arr[2:6] =  [0 1 2 3 4 5]
arr[2:6] =  [2 3 4 5 6 7]
arr[2:6] =  [0 1 2 3 4 5 6 7]
           

再來看看切片

func updateSlice(s []int) {
	s[0] = 100
}

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
	s1 := arr[2:]
	s2 := arr[:]
	fmt.Println(s2) // [0 1 2 3 4 5 6 7]
	fmt.Println("After updateSlice")
	updateSlice(s1)
	fmt.Println(s1) // [100 3 4 5 6 7]
	fmt.Println(arr) // [0 1 100 3 4 5 6 7]
}
           

Slice 本身沒有資料,隻是對底層 array 的一個 view

鞏固一下

// s2 = [0 1 100 3 4 5 6 7]
fmt.Println("reSlice")
s2 = s2[:5]
fmt.Println(s2) // [0 1 100 3 4]
s2 = s2[2:]
fmt.Println(s2) // [100 3 4]
           

這些都了解,下面再看個例子

arr = [...]int{0,1,2,3,4,5,6,7}
s1 = arr[2:6]
s2 = s1[3:6]
fmt.Println("s1 = ",s1)
fmt.Println("s2 = ",s2)
           

這是不是有問題呢? s1是 arr 的第3個到第6個元素,總共4個

s2是不是下标越界了呢

運作一下

s1 =  [2 3 4 5]
s2 =  [5 6 7]
           

再來看看

fmt.Println(s1[4])

panic: runtime error: index out of range

很奇怪對不對,奇怪就對了

記住這個概念

Slice 本身沒有資料,隻是對底層 array 的一個 view

來看個圖

go 語言學習(5)--數組與切片
  • s1為[2 3 4 5],s2為[5 6 7]
  • slice 可以向後擴充,不可以向前擴充
  • s[i]不可以超越len(s),向後擴充不可以超越cap(s)
    go 語言學習(5)--數組與切片
    這就是

    cap

    capacity(容量)

    的概念
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n",
		s1, len(s1), cap(s1))
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",
		s2, len(s2), cap(s2))

// s1=[2 3 4 5],len(s1)=4,cap(s1)=6
// s2=[5 6 7],len(s2)=3,cap(s2)=3
           

go

的數組可以指定起始,結束和

capacity

,而

python

是指定起始,結束和步長,不要混為一談

s1[1:2:3]

指定

cap

為3

slice

起初我也覺得有點繞,多練習就好了

append

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println(s1, s2, s3, s4, s5)

// [2 3 4 5] [5 6] [5 6 10] [5 6 10 11] [5 6 10 11 12]
           

append和其他語言一樣,可以突破

capacity

的限制,依次在數組後面添加元素

擴容的規律

func printSlice(s []int) {
	fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))
}

var s []int
for i := 0; i < 100; i++ {
	printSlice(s)
	s = append(s, 2*i+1)
}

           

貼幾個結果感受下

len=14, cap=16
len=15, cap=16
len=16, cap=16
...
len=31, cap=32
len=32, cap=32
...
len=63, cap=64
len=64, cap=64
len=65, cap=128
len=66, cap=128
           

java

不同,

java

預設負載因子是

0.75

,

go

看的出來是

capacity

滿了才擴容,每次擴容兩倍,是以和

java

一樣,數組最好知道容量,上來就建好

還有一種建立數組的方法

make

// s1 = [2,3,4,5]
s2 = make([]int, 16)
s3 = make([]int, 10, 32)
printSlice(s2)
printSlice(s3)

// len=16, cap=16
// len=10, cap=32
           

後面會經常用到,建立數組和

channel

都很常用

copy(s2, s1)

fmt.Println(s2)

// [2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0]
           

删除數組元素

fmt.Println("Deleting elements from slice")
s2 = append(s2[:3], s2[4:]...)
// 這裡s2[4:]...,...代表不定參(參考第四章函數不定參的講解)
printSlice(s2)

fmt.Println("Poping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println(front)
printSlice(s2)

fmt.Println("Poping from tail")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(tail)
printSlice(s2)

/**
Deleting elements from slice
len=15, cap=16
Poping from front
2
len=14, cap=15
Poping from tail
0
len=13, cap=15
*/
           

總結

  • go

    裡隻有值傳遞
  • 不要直接用 array, 要用切片來操作
  • 注意

    cap

    這個隐含的值,你如果不想别人能通路到切片以外的資料,可以加上

    cap

    比如

    s[1:2:3]

  • 切片隻是對底層

    array

    view

代碼檢視 https://github.com/yejunyu/golearn