昨天介紹了切片的內部運作及隱藏陣列的存在,今天會使用各種範例來複製切片,一起來看看用不同的複製方式與隱藏陣列的關聯吧!
看完昨天的介紹,我只有一個想法:「難道不能複製一個跟本來切片指向不同隱藏陣列的切片嗎?」當然可以!有以下兩種方法:
且當你加入新元素的時候也不用擔心容量問題, Go 的底層切片說他會自己看著辦!
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
範例 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
範例 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
範例 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
範例 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) 是什麼,明天見!