Golang 語言中的 Slice

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

概念


Slice切片是對底層數組Array的封裝,在記憶體中的儲存本質就是數組,體現為連續的記憶體塊,Go語言中的數組定義之後,長度就已經固定了,在使用過程中並不能改變其長度,而Slice就可以看做一個長度可變的數組進行使用,最為關鍵的,是數組在使用的過程中都是值傳遞,將一個數組賦值給一個新變數或作為方法參數傳遞時,是將源數組在記憶體中完全複製了一份,而不是引用源數組在記憶體中的地址,為了滿足記憶體空間的複用和數組元素的值的一致性的應用需求,Slice出現了,每個Slice都是都源數組在記憶體中的地址的一個引用,源數組可以衍生出多個Slice,Slice也可以繼續衍生Slice,而記憶體中,始終只有源數組,當然,也有例外,後邊再說。


用法

1.Slice的定義

Slice可以通過兩種方式定義,一種是從源數組中衍生,一種是通過make函數定義,本質上來說都一樣,都是在記憶體中通過數組的初始化的方式開闢一塊記憶體,將其劃分為若干個小塊用來儲存數組元素,然後Slice就去引用整個或者局部數組元素。

從數組中切片構建Slice:

a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}  s := a[2:8]  fmt.Println(s)//輸出:[3 4 5 6 7 8]

定義一個數組a,截取下標為2到8之間部分(包括2不包括8),構建一個Slice。

通過make函數定義:

s := make([]int, 10, 20)  fmt.Println(s) //輸出:[0 0 0 0 0 0 0 0 0 0]//make函數第一個參數表示構建的數組的類型,第二個參數為數組的長度,第三個參數可選,是slice的容量,預設為第二個參數值

2.Slice的長度和容量


Slice有兩個比較混淆的概念,就是長度和容量,何謂長度?這個長度跟數組的長度是一個概念,即在記憶體中進行了初始化實際存在的元素的個數。何謂容量?如果通過make函數建立Slice的時候指定了容量參數,那記憶體管理器會根據指定的容量的值先劃分一塊記憶體空間,然後才在其中存放有數組元素,多餘部分處於空閑狀態,在Slice上追加元素的時候,首先會放到這塊閒置記憶體中,如果添加的參數個數超過了容量值,記憶體管理器會重新劃分一塊容量值為原容量值*2大小的記憶體空間,依次類推。這個機制的好處在能夠提升運算效能,因為記憶體的重新劃分會降低效能。

a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}  s := a[0:]  s = append(s, 11, 22, 33)  sa := a[2:7]  sb := sa[3:5]  fmt.Println(a, len(a), cap(a))    //輸出:[1 2 3 4 5 6 7 8 9 0] 10 10  fmt.Println(s, len(s), cap(s))    //輸出:[1 2 3 4 5 6 7 8 9 0 11 22 33] 13 20  fmt.Println(sa, len(sa), cap(sa)) //輸出:[3 4 5 6 7] 5 8  fmt.Println(sb, len(sb), cap(sb)) //輸出:[6 7] 2 5  //可以看出,數組的len和cap是永遠相等的,並且是在定義的時候就已經指定的,不能改變。切片s引用這個數組的全部元素,初始長度和容量都為10,繼續追加3個元素後,其長度變為13容量為20,。切片sa截取下標2到7的數組片段,長度為5,容量為8,這個容量的改變規則為原容量值減掉起始下標,此時若追加元素,會覆蓋掉原記憶體位址中存在的值。切片sb截取切片sa下標3到5的數組片段,注意,這裡的下標指的是sa的下標,不是源數組的下標,長度為2,容量為8-3=5。

 

3.Slice是參考型別

上邊已經提到過,Slice是對源數組的一個引用,改變Slice中的元素的值,實質上就是改變源數組的元素的值。

a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}  sa := a[2:7]  sa = append(sa, 100)  sb := sa[3:8]  sb[0] = 99  fmt.Println(a)  //輸出:[1 2 3 4 5 99 7 100 9 0]  fmt.Println(sa) //輸出:[3 4 5 99 7 100]  fmt.Println(sb) //輸出:[99 7 100 9 0]//可以看到,不管是append操作,還是賦值操作,都影響了源數組或者其他引用同一數組的Slice的元素。Slice進行數組引用的時候,其實是將指標指向了記憶體中具體元素的地址,如數組的記憶體位址,事實上是數組中第一個元素的記憶體位址,Slice也是如此。a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}  sa := a[2:7]  sb := sa[3:8]  fmt.Printf("%p\n", sa) //輸出:0xc084004290  fmt.Println(&a[2], &sa[0])      //輸出:0xc084004290 0xc084004290  fmt.Printf("%p\n", sb) //輸出:0xc0840042a8  fmt.Println(&a[5], &sb[0])      //輸出:0xc0840042a8 0xc0840042a8

4.Slice引用傳遞發生“意外”


上邊我們一直在說,Slice是參考型別,指向的都是記憶體中的同一塊記憶體,不過在實際應用中,有的時候卻會發生“意外”,這種情況只有在像切片append元素的時候出現,Slice的處理機制是這樣的,當Slice的容量還有閒置時候,append進來的元素會直接使用閒置容量空間,但是一旦append進來的元素個數超過了原來指定容量值的時候,記憶體管理器就是重新開闢一個更大的記憶體空間,用於儲存多出來的元素,並且會將原來的元素複製一份,放到這塊新開闢的記憶體空間。

a := []int{1, 2, 3, 4}  sa := a[1:3]  fmt.Printf("%p\n", sa) //輸出:0xc0840046e0  sa = append(sa, 11, 22, 33)  fmt.Printf("%p\n", sa) //輸出:0xc084003200//可以看到執行了append操作後,記憶體位址發生了變化,說明已經不是引用傳遞。

  總之,slice是封裝過的array,slice用起來真爽,不需要像c語言那樣 超過數組size,remalloc size後copy

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.