日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
你真的理解 Golang 切片嗎?全切片表達(dá)式及切片使用技巧

簡(jiǎn)介

Golang 中通常的 slice 語法是 a[low:high],您可能很熟悉。還有另一種切片語法,形式為 a[low:high:max],它采用三個(gè)索引而不是兩個(gè)索引。第三索引 max 是做什么的?

創(chuàng)新互聯(lián)建站主要為客戶提供服務(wù)項(xiàng)目涵蓋了網(wǎng)頁視覺設(shè)計(jì)、VI標(biāo)志設(shè)計(jì)、成都全網(wǎng)營銷推廣、網(wǎng)站程序開發(fā)、HTML5響應(yīng)式成都網(wǎng)站建設(shè)手機(jī)網(wǎng)站開發(fā)、微商城、網(wǎng)站托管及網(wǎng)站維護(hù)公司、WEB系統(tǒng)開發(fā)、域名注冊(cè)、國內(nèi)外服務(wù)器租用、視頻、平面設(shè)計(jì)、SEO優(yōu)化排名。設(shè)計(jì)、前端、后端三個(gè)建站步驟的完善服務(wù)體系。一人跟蹤測(cè)試的建站服務(wù)標(biāo)準(zhǔn)。已經(jīng)為成都會(huì)所設(shè)計(jì)行業(yè)客戶提供了網(wǎng)站制作服務(wù)。

提示: 不是 Python 切片語法 a[low:high:step] 中的 step 步長(zhǎng)索引。

答: 第三個(gè)索引用于設(shè)置切片的容量!在 Golang 規(guī)范中稱為 “全切片表達(dá)式”。

了解 Golang 切片

為了理解為什么要在 Golang 中加入這個(gè)功能,以及它的作用,讓我們從數(shù)組和指針開始。

越界錯(cuò)誤在 C 語言程序中很常見,Golang 通過內(nèi)置的運(yùn)行時(shí)邊界檢查器來緩解這個(gè)問題。數(shù)組的邊界檢查很簡(jiǎn)單,因?yàn)?Golang 的數(shù)組是固定長(zhǎng)度的,然而,指針的邊界檢查就不那么簡(jiǎn)單了,因?yàn)橹羔樀倪吔鐩]有明確定義。Golang 中的切片只是解決指針的邊界檢查的一種方法。

Golang 不使用普通的指針來訪問數(shù)組元素,而是用一個(gè)長(zhǎng)度字段來擴(kuò)充指針;結(jié)果(帶長(zhǎng)度的指針)被稱為 "切片",或在其他地方稱為 "胖指針"。有了長(zhǎng)度字段,運(yùn)行時(shí)的邊界檢查就很容易了。

Golang 的切片不僅僅是帶長(zhǎng)度的指針,它們還有一個(gè) "容量 "字段,因?yàn)樵黾觿?dòng)態(tài)分配的數(shù)組是一個(gè)很常見的任務(wù)。分片的容量也作為分片表達(dá)式 a[low:high] 的邊界檢查器——切片的末端不能超過其容量。

理解 a[low:high:max]

切片索引表達(dá)式由長(zhǎng)度字段進(jìn)行邊界檢查,長(zhǎng)度字段可以通過分片來減少,以提供所需的邊界檢查。

同樣地,人們可能會(huì)想,是否有可能減少片斷的容量,以加強(qiáng)對(duì)切片表達(dá)式 a[low:high] 的邊界檢查。例如,下面的表達(dá)式將一個(gè)切片的容量減少到它的長(zhǎng)度:

a = a[0:len(a):len(a)]

在這之后,切片 a 被限制在它自己的范圍內(nèi),切片結(jié)束后的元素不能被訪問或修改,即使你不小心重新分片或追加到它上面。

這個(gè)技巧對(duì)于從不可改變的數(shù)組中返回一個(gè)切片很有用;如果你不小心追加到所謂的不可改變的切片中,就會(huì)強(qiáng)制復(fù)制,并且沒有數(shù)據(jù)被覆蓋,因?yàn)橐呀?jīng)沒有容量了。

這種形式的分片表達(dá)式在 Golang 規(guī)范中被稱為 "完整分片表達(dá)式"(full slice expression)。

切片的使用技巧

定義切片:

type SeriesInt64 struct {
   values   []int64
}

自從引入內(nèi)置的 append 以來,Go 1 中刪除的 container/vector 包的大部分功能都可以使用 append 和 copy 來復(fù)制。

自從引入泛型以來,golang.org/x/exp/slices 包中提供了其中幾個(gè)函數(shù)的泛型實(shí)現(xiàn)。

以下是矢量方法及其切片操作:

AppendVector

a = append(a, b...)

Copy

b := make([]T, len(a))
copy(b, a)

// These two are often a little slower than the above one,
// but they would be more efficient if there are more
// elements to be appended to b after copying.
b = append([]T(nil), a...)
b = append(a[:0:0], a...)

// This one-line implementation is equivalent to the above
// two-line make+copy implementation logically. But it is
// actually a bit slower (as of Go toolchain v1.16).
b = append(make([]T, 0, len(a)), a...)

封裝成函數(shù),可以這樣寫:

func (s *SeriesInt64) copy() *SeriesInt64 {
    if len(s.values) == 0 {
        return &SeriesInt64{
            values:[]int64{},
        }
    }
    // Copy slice
    x := s.values[0 : len(s.values)]
    newSlice := append(x[:0:0], x...)
    return &SeriesInt64{
        values: newSlice,
    }
}

Cut

a = append(a[:i], a[j:]...)

Delete

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

封裝后:

func (s *SeriesInt64) remove(row int) {
    s.values = append(s.values[:row], s.values[row+1:]...)
}

Delete without preserving order

a[i] = a[len(a)-1] 
a = a[:len(a)-1]

NOTE 如果元素的類型是指針或帶指針字段的結(jié)構(gòu)體,需要進(jìn)行垃圾回收,上述 Cut 和 Delete 的實(shí)現(xiàn)存在潛在的內(nèi)存泄漏問題:一些有值的元素仍然被切片 a 引用,從而無法收集。下面的代碼可以解決這個(gè)問題:

Cut

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
 a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

Delete

copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]

Delete without preserving order

a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]

Expand

Insert n elements at position i:

a = append(a[:i], append(make([]T, n), a[i:]...)...)

Extend

Append n elements:

a = append(a, make([]T, n)...)

Extend Capacity

Make sure there is space to append n elements without re-allocating:

if cap(a)-len(a) < n {
 a = append(make([]T, 0, len(a)+n), a...)
}

Filter (in place)

n := 0
for _, x := range a {
 if keep(x) {
  a[n] = x
  n++
 }
}
a = a[:n]

Insert

a = append(a[:i], append([]T{x}, a[i:]...)...)

注意:第二個(gè)追加創(chuàng)建一個(gè)新的切片,它有自己的底層存儲(chǔ),并將 a[i:] 中的元素復(fù)制到該切片,然后這些元素被復(fù)制回切片 a(由第一個(gè)追加)。使用替代方法可以避免創(chuàng)建新切片(以及內(nèi)存垃圾)和第二個(gè)副本:

Insert

s = append(s, 0 /* use the zero value of the element type */)
copy(s[i+1:], s[i:])
s[i] = x

封裝后:

func (s *SeriesInt64) insert(row int, val int64) {
    s.values = append(s.values, 0)
    copy(s.values[row+1:], s.values[row:])
    s.values[row] = val
}

InsertVector

a = append(a[:i], append(b, a[i:]...)...)

// The above one-line way copies a[i:] twice and
// allocates at least once.
// The following verbose way only copies elements
// in a[i:] once and allocates at most once.
// But, as of Go toolchain 1.16, due to lacking of
// optimizations to avoid elements clearing in the
// "make" call, the verbose way is not always faster.
//
// Future compiler optimizations might implement
// both in the most efficient ways.
//
// Assume element type is int.
func Insert(s []int, k int, vs ...int) []int {
 if n := len(s) + len(vs); n <= cap(s) {
  s2 := s[:n]
  copy(s2[k+len(vs):], s[k:])
  copy(s2[k:], vs)
  return s2
 }
 s2 := make([]int, len(s) + len(vs))
 copy(s2, s[:k])
 copy(s2[k:], vs)
 copy(s2[k+len(vs):], s[k:])
 return s2
}

a = Insert(a, i, b...)

Push

a = append(a, x)

Pop

x, a = a[len(a)-1], a[:len(a)-1]

Push Front/Unshift

a = append([]T{x}, a...)

Pop Front/Shift

x, a = a[0], a[1:]

Prepending

func (s *SeriesInt64) prepend(val int64) {
    if cap(s.values) > len(s.values) {
        s.values = s.values[:len(s.values)+1]
        copy(s.values[1:], s.values)
        s.values[0] = val
        return
    }
    // No extra capacity so a new slice needs to be allocated:
    s.insert(0, val)
}

圖片來源:

  • Go Slice Tricks Cheat Sheet

網(wǎng)頁標(biāo)題:你真的理解 Golang 切片嗎?全切片表達(dá)式及切片使用技巧
網(wǎng)頁URL:http://www.dlmjj.cn/article/cocijdi.html