iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 8
1
Modern Web

從無到有,使用 Go 開發應用程式系列 第 8

Slice Type

Slice 跟陣列使用起來很像,而最大的不同是,陣列是值, Slice 是參考到一個陣列。

建立

要建立一個全新的 Slice 有兩種方法,一個是使用 make 函式:

package main

import "fmt"

func main() {
	slice := make([]int, 5)

	fmt.Println(slice)  // [0 0 0 0 0]
}

另一個方法則是指定初值,雖然用法跟陣列很像,但形態不一樣就不能拿來一起比較。

package main

import "fmt"
import "reflect"

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

	fmt.Println(slice)  // [1 2 3 4 5]

	arr := [...]int{1, 2, 3, 4, 5}

	fmt.Println(reflect.TypeOf(slice))  // []int
	fmt.Println(reflect.TypeOf(arr))    // [5]int
	fmt.Println(arr == slice)           // invalid operation: arr == slice (mismatched types [5]int and []int)
}

Slice 是參考到一個陣列,可以看下面這個範列了解:

package main

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4, 5}
	ref := slice

	fmt.Println(slice)  // [1 2 3 4 5]
	fmt.Println(ref)    // [1 2 3 4 5]

	slice[0] = 100

	fmt.Println(slice)  // [100 2 3 4 5]
	fmt.Println(ref)    // [100 2 3 4 5]

	ref[4] = 500

	fmt.Println(slice)  // [100 2 3 4 500]
	fmt.Println(ref)    // [100 2 3 4 500]
}

操作

我們可以對 Slice 做一些操作,如 len 函式可以查長度, cap 可以查參考的陣列有多少容量:

package main

import "fmt"

func main() {
	slice := []int{1, 2, 3}

	fmt.Println(len(slice))  // 3
	fmt.Println(cap(slice))  // 3
}

append 函式可以追加新元素在 Slice 最後面,下面是一個小範例:

package main

import "fmt"

func main() {
	slice1 := []int{1, 2, 3}

	fmt.Println(slice1)       // [1 2 3]
	fmt.Println(len(slice1))  // 3
	fmt.Println(cap(slice1))  // 3

	slice2 := append(slice1, 10)

	fmt.Println(slice2)       // [1 2 3 10]
	fmt.Println(len(slice2))  // 4
	fmt.Println(cap(slice2))  // 6

	slice3 := append(slice2, 20)

	fmt.Println(slice3)       // [1 2 3 10 20]
	fmt.Println(len(slice3))  // 5
	fmt.Println(cap(slice3))  // 6

	slice3[0] = 100
	fmt.Println(slice1)       // [1 2 3]
	fmt.Println(slice2)       // [100 2 3 10]
	fmt.Println(slice3)       // [100 2 3 10 20]
}

上面可以觀察到 slice1 加入新元素產生出 slice2 有發生長度與容量的變化(長度 + 1 ,容量 * 2),並且最後面 slice1slice2 的值並沒有參考到同個陣列。

另外 slice2 加入新元素產生出 slice3 只有長度 + 1 而已,最後面的值也有參考到同個陣列。

由此可知,當 Slice 新增元素超過了容量的時候,它會產生新的陣列,且容量有兩倍,給新的 Slice 參考;而容量夠用的時候,則不會產生新陣列。

copy 函式可以複製內容到另一個 Slice 裡,如下:

package main

import "fmt"

func main() {
	src := []int{1, 2, 3}
	dst1 := make([]int, 2)
	dst2 := make([]int, 3)
	dst3 := make([]int, 4)

	fmt.Println(src)     // [1 2 3]
	fmt.Println(dst1)    // [0 0]
	fmt.Println(dst2)    // [0 0 0]
	fmt.Println(dst3)    // [0 0 0 0]

	copy(dst1, src)
	copy(dst2, src)
	copy(dst3, src)

	fmt.Println(src)     // [1 2 3]
	fmt.Println(dst1)    // [1 2]
	fmt.Println(dst2)    // [1 2 3]
	fmt.Println(dst3)    // [1 2 3 0]

	src[0] = 100

	fmt.Println(src)     // [100 2 3]
	fmt.Println(dst1)    // [1 2]
	fmt.Println(dst2)    // [1 2 3]
	fmt.Println(dst3)    // [1 2 3 0]
}

複製時,即使長度不一還是會執行成功,只是會沒有複製完全。

從陣列或 Slice 產生 Slice

除了從頭建一個新的 Slice 外,也可以從陣列或 Slice 上產生新的 Slice ,以下是簡單的範例

package main

import "fmt"

func main() {
	arr := [...]int{1, 2, 3, 4, 5}

	slice := arr[1:4]

	fmt.Println(slice)        // [2 3 4]
	fmt.Println(len(slice))   // 3
	fmt.Println(cap(slice))   // 4

	slice2 := slice[1:3]

	fmt.Println(slice2)       // [3 4]
	fmt.Println(len(slice2))  // 2
	fmt.Println(cap(slice2))  // 3

	slice2[0] = 300

	fmt.Println(arr)          // [1 2 300 4 5]
	fmt.Println(slice)        // [2 300 4]
	fmt.Println(slice2)       // [300 4 5]
}

[1:4] 代表的意思是,從「第 1 個元素開始,到第 4 個元素,不含第 4 個元素」,因此會取得 [2 3 4] 三個元素。而容量會從第 1 個元素開始,一直到結尾,以上例來說就是 4

下一個 [1:3] 相信就不難懂了,不過它會從 [2 3 4] 這個 Slice 取元素,所以會取到的是 [3 4] ,容量是 3

最後,因為沒有使用 append 函式,所以它們都參考到第一個陣列。

另外也可以使用 [:] 來取得全部陣列的內容。

參考資料


上一篇
Array Type
下一篇
Map Type
系列文
從無到有,使用 Go 開發應用程式30

尚未有邦友留言

立即登入留言