iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0

前言

在GO當中,有兩種資料結構是能夠儲存多項相同型態資料的,分別為ArraySlice

  • Array: List的長度是固定的,處於原生型別,對開發者來說使用次數較少。
  • Slice: List的長度為不固定,使用[]來進行定義,像是[]int 就是int slice,指的是slice裡頭的元素皆為int。

無論是Array或是Slice,皆必須指定裡頭資料的型別,且裡頭資料的型別必須一致。

陣列 Array

陣列的長度在宣告後無法改變,若在某些index未賦予值,則該index值為此type的預設值,像是int的預設值就是0

package main

import "fmt"

func main() {
	var a [5]int
	a[0] = 10
	a[1] = 100
	a[2] = 1000
	fmt.Println(a)
}

運算可得結果

[10 100 1000 0 0]

此外

  • 值也可以一開始就給,可部分宣告值,沒值的預設為零值
  • Array可以只選用其中的某段資料
  • 可以利用[...]來讓系統設定array長度
package main

import "fmt"

func main() {
	b := [5]int{1, 10, 100}
	fmt.Println(b)
	
	c := [5]int{2, 20, 200, 2000}
	fmt.Println(c[1:3])
	
	d := [...]int{3, 30, 300, 3000}
	fmt.Println(d)
}

運算後可得結果

[1 10 100 0 0]
[20 200]
[3 30 300 3000]
4

array 地雷

e:= []int{1,2}

[]當中沒有任何東西的宣告是屬於Slice,而不是省略長度的ArrayArray一定需要賦予長度!

切片 Slice

An array has a fixed size.
A slice, on the other hand, is a dynamically-sized,
flexible view into the elements of an array.

Slice可以視為長度不固定的Array

宣告時中括號[]裡為空

Slice拆開來看,這玩意包含了三樣東西:

  • 長度len,指的是現在的長度
  • 容量cap,指的是最大能容納的長度
  • 指針ptr,透過指針,我們能與別人共用同個地方

建立slice

以下為幾種宣告Slice的方式

package main
import (
    "fmt"
)
func main() {
    a := make([]int, 2)
    fmt.Println(a, len(a), cap(a), len(a) == 0, a == nil)

    b := make([]int, 2, 4)
    fmt.Println(b, len(b), cap(b), len(b) == 0, b == nil)

    var c = []int{} 
    fmt.Println(c, len(c), cap(c), len(c) == 0, c == nil)

    var d []int 
    fmt.Println(d, len(d), cap(d), len(d) == 0, d == nil)

    e := []string{"ironman", "2021"}
    fmt.Println(e, len(e), cap(e), len(e) == 0, e == nil)
}

運算後得到結果

[0 0] 2 2 false false
[0 0] 2 4 false false
[] 0 0 true false
[] 0 0 true true
[ironman 2021] 2 2 false false
  • a: 設定len為2,沒給容量。但因最大容量長度不可能小於2,故系統設定為2
  • b: 設定len為2,容量為4。
  • c: 初始化的slice
  • d: 尚未初始化的slice,此時等同於nil
  • e: 直接賦予值的slice

slice深層解析

Slice並不是真正存值,而是透過指針指到更下面的地方,記憶體中的某個陣列

這也是為什麼說可以跟別人共用的原因。

如果SliceA跟SliceB都是一樣的值,底層陣列只要儲存一份資料,這樣也能節省記憶體空間。
https://ithelp.ithome.com.tw/upload/images/20210921/20129737321DN2ETdW.png

package main

import "fmt"

func main() {
	a := make([]int, 0, 10) 
	b := append(a, 1, 2, 3)
	_ = append(a, 99, 88, 77)
	fmt.Println(b)

	aa := make([]int, 0, 2)
	bb := append(aa, 1, 2, 3)
	_ = append(aa, 99, 88, 77)
	fmt.Println(bb)

}

運行後可得

[99 88 77]
[1 2 3]

足夠容量中沒發生災情,但是在超小容量中卻因為後來的_做了append

導致我的aa中的值也被改了?

沒錯,這就是可以看到pointer的地方,原因在於底層的陣列被後來的_蓋掉了。

Summary

這一次所介紹的ArraySlice就類型與用途來說就完全與Python的List或是Tuple完全不同了,這邊就會需要讀者適應一下,無論是固定類型長度,或者是使用規則方面,但這樣的學習與使用方式,也會讓我們更暸解這兩種資料結構的底層運作邏輯。


上一篇
Day5 Type
下一篇
Day7 Map and Struct
系列文
fmt.Println("從零開始的Golang生活")30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言