golang 資料二 (切片)

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

在項目開發過程中,更多的情境是需要一個長度可以動態更新的資料存放區結構,切片本身並非是動態數組或數組指標,他內部通過指標引用底層數組,並設定相關屬性將資料讀寫操作限定在指定地區內。比如:

/runtime/slice.gotype聽slice聽struct聽{array聽unsafe.Pointerlen聽聽聽intcap聽聽聽int}

切片初始化

切片有兩種基本初始化方式:

切片可以通過內建的make函數來初始化,初始化時len=cap,一般使用時省略cap參數,預設和len參數相同,在追加元素時,如果容量cap不足時,將按len的2倍動態擴容。

通過數組來初始化切片,以開始和結束索引位置來確定最終所引用的數組片段。

//make([]T,聽len,聽cap)聽//T是切片的資料的類型,len表示length,cap表示capacity{聽聽聽聽s聽:=聽make([]int,5)聽聽聽聽聽聽//len:聽5聽聽cap:聽5聽聽聽聽s聽:=聽make([]int,5,10)聽聽聽聽//len:聽5聽聽cap:聽10聽聽聽聽s聽:=聽[]int{1,2,3}}聽{聽聽聽聽arr聽:=聽[...]int{0,1,2,3,4,5,6,7,8,9}聽聽聽聽s1聽:=聽arr[:]聽聽聽聽s2聽:=聽arr[2:5]聽聽聽聽s3聽:=聽arr[2:5:7]聽聽聽聽s4聽:=聽arr[4:]聽聽聽聽s5聽:=聽arr[:4]聽聽聽聽s6聽:=聽arr[:4:6]聽聽聽聽fmt.Println("s1:聽",s1,聽len(s1),cap(s1))聽聽聽聽fmt.Println("s2:聽",s2,聽len(s2),cap(s2))聽聽聽聽fmt.Println("s3:聽",s3,聽len(s3),cap(s3))聽聽聽聽fmt.Println("s4:聽",s4,聽len(s4),cap(s4))聽聽聽聽fmt.Println("s5:聽",s5,聽len(s5),cap(s5))聽聽聽聽fmt.Println("s6:聽",s6,聽len(s6),cap(s6))}輸出:s1:聽聽[0聽1聽2聽3聽4聽5聽6聽7聽8聽9]聽10聽10s2:聽聽[2聽3聽4]聽3聽8s3:聽聽[2聽3聽4]聽3聽5s4:聽聽[4聽5聽6聽7聽8聽9]聽6聽6s5:聽聽[0聽1聽2聽3]聽4聽10s6:聽聽[0聽1聽2聽3]聽4聽6

通過上例說明cap 是表示切片所引用數組片段的真實長度,len是表示已經賦過值的最大下標(索引)值加1.

注意下面兩種初始化方式的區別:

{聽聽聽聽var聽a聽聽[]int聽聽聽聽b聽:=聽[]int{}聽聽聽聽fmt.Println(a==nil,b==nil)聽聽聽聽fmt.Printf("a:聽%#v\n",聽(*reflect.SliceHeader)(unsafe.Pointer(&a)))聽聽聽聽fmt.Printf("b:聽%#v\n",聽(*reflect.SliceHeader)(unsafe.Pointer(&b)))聽聽聽聽fmt.Printf("a聽size聽%d\n",聽unsafe.Sizeof(a))聽聽聽聽fmt.Printf("b聽size聽%d\n",聽unsafe.Sizeof(b))}輸出:true聽falsea:聽&reflect.SliceHeader{Data:0x0,聽Len:0,聽Cap:0}b:聽&reflect.SliceHeader{Data:0x5168b0,聽Len:0,聽Cap:0}a聽size聽24b聽size聽24說明:1.聽變數b的內部指標被賦值,即使該指標指向了runtime.zerobase,但它依然完成了初始化操作2.聽變數a表示一個未初始化的切片對象,切片本身依然會分配所需的記憶體

切片之間不支援邏輯運算子,僅能判斷是否為nil,比如:

{聽聽聽聽var聽a聽聽[]int聽聽聽聽b聽:=聽[]int{}聽聽聽聽fmt.Println(a==b)聽//invalid聽operation:聽a聽==聽b聽(slice聽can聽only聽be聽compared聽to聽nil)}

reslice

在原slice的基礎上進行建立slice,建立的slice依舊指向原底層數組,新建立的slice不能超出原slice

的容量,但是不受其長度限制,並且如果修改建立slice的值,對所有關聯的切片都有影響,比如:

{聽聽聽聽s聽:=聽[]string{"a","b","c","d","e","f","g"}聽聽聽聽s1聽:=聽s[1:3]聽聽聽聽聽聽聽聽//b,c聽聽聽聽fmt.Println(s1,聽len(s1),cap(s1))聽聽聽聽s1_1聽:=聽s1[2:5]聽聽聽聽聽聽聽聽//c,d,e聽聽聽聽fmt.Println(s1_1,聽len(s1_1),cap(s1_1))}輸出:[b聽c]聽2聽6[d聽e聽f]聽3聽4

append

向切片尾部追加資料,返回新的切片對象; 資料被追加到原底層數組,如果超出cap限制,則為新切片對象重新分配數組,新分配的數組cap是原數組cap的2倍,比如:

{聽聽聽聽s聽:=聽make([]int,0,5)聽聽聽聽s聽=聽append(s聽,聽1)聽聽聽聽s聽=聽append(s聽,聽2,3,4,5)聽聽聽聽fmt.Printf("%p,聽%v,聽%d\n",聽s,s,聽cap(s))聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽s聽=聽append(s聽,聽6)聽聽聽聽聽聽聽//重新分配記憶體聽聽聽聽fmt.Printf("%p,聽%v,聽%d\n",聽s,聽s,聽cap(s))}輸出:0xc420010210,聽[1聽2聽3聽4聽5],聽50xc4200140a0,聽[1聽2聽3聽4聽5聽6],聽10

如果是向nil切片追加資料,則會高頻率的重新分配記憶體和資料複製,比如:

{聽聽聽聽var聽s聽[]int聽聽聽聽fmt.Printf("%p,聽%v,聽%d\n",聽s,s,聽cap(s))聽聽聽聽for聽i:=聽0;聽i聽<聽10;i++{聽聽聽聽聽聽聽聽s聽=聽append(s,聽i)聽聽聽聽聽聽聽聽fmt.Printf("%p,聽%v,聽%d\n",聽s,聽s,聽cap(s))聽聽聽聽}}

所以為了避免程式運行中的頻繁的資源開銷,在某些情境下建議預留出足夠多的空間。

copy

兩個slice之間複製資料時,允許指向同一個底層數組,並允許目標區間重疊。最終複製的長度以較短的切片長度(len)為準,比如:

{聽聽聽聽s1聽:=聽[]int{0,聽1,聽2,聽3,聽4,聽5聽,6}聽聽聽聽s2聽:=聽[]int{7,聽8聽,9}聽聽聽聽copy(s1,s2)聽聽聽聽fmt.Println(s1,len(s1),cap(s1))聽聽聽聽s1聽=聽[]int{0,聽1,聽2,聽3,聽4,聽5聽,6}聽聽聽聽s2聽=聽[]int{7,聽8聽,9}聽聽聽聽聽copy(s2,s1)聽聽聽聽fmt.Println(s2,len(s2),cap(s2))}

那麼可不可以在同一切片之間複製資料呢?

在項目開發過程中,如果slice長時間引用一個大數組中很小的片段,那麼建議建立一個獨立的切片,並複製出所需的資料,以便原數組記憶體可以被gc及時釋最佳化回收。

本文出自 “博學於文,約之於禮” 部落格,轉載請與作者聯絡!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.