iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0
自我挑戰組

跟著 Go 實戰聖經 一起自學 Go系列 第 16

DAY 16 Go 語言 的複合型別 - 切片 (slice) 不同的複製方式與隱藏陣列的關聯

  • 分享至 

  • xImage
  •  

昨天介紹了切片的內部運作及隱藏陣列的存在,今天會使用各種範例來複製切片,一起來看看用不同的複製方式與隱藏陣列的關聯吧!

複製一個跟本來切片指向不同隱藏陣列的切片

看完昨天的介紹,我只有一個想法:「難道不能複製一個跟本來切片指向不同隱藏陣列的切片嗎?」當然可以!有以下兩種方法:

1. 用 append() 把本來的切片附加到新切片

且當你加入新元素的時候也不用擔心容量問題, Go 的底層切片說他會自己看著辦!

2. 用內建函式 copy() 把本來的切片複製到新切片

copy(<目標切片>, <來源切片>)

但特別注意 Go 不會改變目標切片的容量大小,所以當使用 copy() 函式時,要先確認目標切片有的容量可以容納來源切片。

以下就用各種狀況的範例來演示切片與底層陣列的關聯:

指向同一個隱藏陣列

範例 2:

package main

import (
	"fmt"
)

var slice1 = []int{1,2,3,4,5} // 在外層定義一個 slice1 的切片

func sameHideArray() (int,int,int){
    slice2 := slice1  // 複製 slice1 切片為 slice2 (會使兩者指向同一個隱藏陣列)
    slice3 := slice1[:]  // 從 slice1 擷取原本長度的切片為 slice2 (會使兩者指向同一個隱藏陣列)

    slice2[0] = 77  // 更改 slice2 切片索引值 0 的值為 77
    return slice1[0], slice2[0], slice3[0]
}

func main() { 
    s1,s2,s3:=sameHideArray()

	fmt.Println("指向同一個隱藏陣列,且更改中一個的值:","slice1[0]=",s1,"; slice2[0]=",s2,"; slice3[0]=",s3)  // 因為三者皆指向同一個底層隱藏陣列,故更換其中一個的值,其餘切片也會受影響
}

範例 2(執行結果):

指向同一個隱藏陣列,且更改中一個的值: slice1[0]= 77 ; slice2[0]= 77 ; slice3[0]= 77

使用內建函式 append(),加入超過原本容量的的值,使其指向不同隱藏陣列

範例 3:

package main

import (
	"fmt"
)

var slice1 = []int{1,2,3,4,5} // 在外層定義一個 slice1 的切片

func appendToNewArray() (int,int){
    slice2 := slice1  // 複製 slice1 切片為 slice2 (會使兩者指向同一個隱藏陣列)
    slice2 = append(slice2, 6,7,8) // 使用內建函式 append() 將 slice2 新增元素值 6.7.8 ,本來跟 slice1 指向相同隱藏陣列,但因為超過本來的容量會陣列置換,所以建立一個跟本來不同的隱藏陣列 slice2

    slice2[0] = 77  // 更改 slice2 切片索引值 0 的值為 77
    return slice1[0], slice2[0]
}

func main() { 
    s1,s2:=appendToNewArray()

	fmt.Println("指向新的隱藏陣列,且更改 slice2 中的值:","slice1[0]=",s1,"; slice2[0]=",s2)  // 因為兩者已經指向不同底層隱藏陣列,故更換其中一個的值,原本切片不會受影響
}

範例 3(執行結果):

指向新的隱藏陣列,且更改 slice2 中的值: slice1[0]= 1 ; slice2[0]= 77

使用內建函式 append(),加入不超過原本容量的的值,會指向同個隱藏陣列

範例 4:

package main

import (
	"fmt"
)

var slice1 = make([]int, 5, 10) // 在外層定義一個長度為 5 ,容量為 10 的初始化切片

func appendToOriginArray() (int,int){
    slice2 := slice1  // 複製 slice1 切片為 slice2 (會使兩者指向同一個隱藏陣列)
    slice2 = append(slice2, 6,7,8) // 使用內建函式 append() 將 slice2 新增元素值 6.7.8 因為沒有超過容量,所以會共用同一個隱藏陣列

    slice2[0] = 77  // 更改 slice2 切片索引值 0 的值為 77
    return slice1[0], slice2[0]
}

func main() { 
    s1,s2:=appendToOriginArray()

	fmt.Println("指向同個隱藏陣列,且更改 slice2 中的值:","slice1[0]=",s1,"; slice2[0]=",s2)  // 因為兩者皆指向同一個底層隱藏陣列,故更換其中一個的值,其餘切片也會受影響
}

範例 4(執行結果):

指向同個隱藏陣列,且更改 slice2 中的值: slice1[0]= 77 ; slice2[0]= 77

使用內建函式 copy(),使其指向不同隱藏陣列

範例 5:

package main

import (
	"fmt"
)

var slice1 = []int{1,2,3,4,5} // 在外層定義一個 slice1 的切片

func appendToOriginArray() (int,int){
    slice2 := make([]int, 5) // 定義一個長度為 5 ,容量為 10 的初始化切片 slice2
    copy(slice2, slice1) // 使用內建函式 copy() 將 slice2 當作目標切片, slice1 的值複製進 slice2 ,因為內建函式 copy() 會使其用不同隱藏陣列

    slice2[0] = 77  // 更改 slice2 切片索引值 0 的值為 77
    return slice1[0], slice2[0]
}

func main() { 
    s1,s2:=appendToOriginArray()

	fmt.Println("指向不同隱藏陣列,且更改 slice2 中的值:","slice1[0]=",s1,"; slice2[0]=",s2)  // 因為兩者指向不同一個底層隱藏陣列,故更換其中一個的值,其餘切片不會受影響
}

範例 5(執行結果):

指向不同隱藏陣列,且更改 slice2 中的值: slice1[0]= 1 ; slice2[0]= 77

使用內建函式 copy(),使將長的切片複製到短的切片中

範例 6:

package main

import (
	"fmt"
)

var slice1 = []int{1,2,3} // 在外層定義一個長度為 3 的切片

func appendToOriginArray() ([]int,[]int,int){
    slice2 := make([]int, 5) // 定義一個長度為 5 ,容量為 10 的初始化切片 slice2
    copylen := copy(slice1, slice2) // 使用內建函式 copy() 將 slice1 當作目標切片, slice2 的值複製進 slice1 ,因為內建函式 copy() 會使其用不同隱藏陣列

    slice2[0] = 77  // 更改 slice2 切片索引值 0 的值為 77
    return slice1, slice2,copylen
}

func main() { 
    s1,s2,len:=appendToOriginArray()

	fmt.Println("指向不同隱藏陣列,且更改 slice2 中的值:","slice1=",s1,"; slice2=", s2, "; 複製的元素量=", len)  // 因為兩者指向不同一個底層隱藏陣列,故更換其中一個的值,其餘切片不會受影響,且因為 slice1 長度比 slice2 短,故只會從 slice2 複製 3 個元素到 slice1 
}

範例 6(執行結果):

指向不同隱藏陣列,且更改 slice2 中的值: slice1= [0 0 0] ; slice2= [77 0 0 0 0] ; 複製的元素量= 3

補充
copy() 函式會以來源切片和目標切片中較短切片的長度來複製元素,且可以有一個回傳值為,複製的元素量。
正因為還需要注意切片長度,故我們較常使用 append() 取代 copy() 來複製切片。

為了避免我們建立太多空陣列,再使用 append() 加入新元素,我們也可以使用以下簡寫用法:

slice1 := []int{1, 2, 3, 4, 5}
slice2 := append(slice1[:0:0], slice1...)

slice1[:0:0] 分解一下:

<切片>[<起始索引值>:<結束索引值(不含)>:<容量>]

slice1[:0:0] 其實就指向原陣列,但因為容量設為 0 ,所以當 append 其他值進去之後,就會自動換到新的底層陣列,如此一來便可以節省設置空切片的空間。

今天將完整的切片介紹完畢,那我們明天繼續學習在 Go 語言中的 映射表(map) 是什麼,明天見!


上一篇
DAY 15 Go 語言 的複合型別 - 切片 (slice) 的內部運作及隱藏陣列
下一篇
DAY 17 Go 語言 的複合型別 - 映射表 (map)
系列文
跟著 Go 實戰聖經 一起自學 Go30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言