<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go語(yǔ)言中切片操作的那些技巧

          共 7050字,需瀏覽 15分鐘

           ·

          2021-04-18 22:03

          本文翻譯自官方wiki,整理了Go語(yǔ)言中關(guān)于切片操作的一些技巧。

          備注:由于行文需要,一些細(xì)節(jié)與原文存在些許出入。

          切片操作常用技巧

          復(fù)制

          將切片a中的元素復(fù)制到切片b中。

          最簡(jiǎn)單的、最常用的方法就是使用內(nèi)置的copy函數(shù)。

          b = make([]T, len(a))  // 一次將內(nèi)存申請(qǐng)到位
          copy(b, a)

          除了使用內(nèi)置的copy函數(shù)外,還有下面兩種使用append函數(shù)復(fù)制切片的方法。

          b = append([]T(nil), a...)
          b = append(a[:0:0], a...)

          這兩種方法通常比使用copy函數(shù)復(fù)制的方法要慢一些,但是如果在復(fù)制之后有更多的元素要添加到b中,那么它們的效率會(huì)更高。

          剪切

          將切片a中索引i~j位置的元素剪切掉。

          可以按照下面的方式,使用append函數(shù)完成。

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

          刪除

          將切片a中索引位置為i的元素刪除。

          同樣可以按照上面剪切的方式使用append函數(shù)完成刪除操作。

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

          或者搭配copy函數(shù)使用切片表達(dá)式完成刪除操作。

          a = a[:i+copy(a[i:], a[i+1:])]

          此外,如果只需要?jiǎng)h除掉索引為i的元素,無需保留切片元素原有的順序,那么還可以使用下面這種簡(jiǎn)單的方式進(jìn)行刪除。

          a[i] = a[len(a)-1]  // 將最后一個(gè)元素移到索引i處
          a = a[:len(a)-1]    // 截掉最后一個(gè)元素

          剪切或刪除操作可能引起的內(nèi)存泄露

          需要特別注意的是。如果切片a中的元素是一個(gè)指針類型或包含指針字段的結(jié)構(gòu)體類型(需要被垃圾回收),上面剪切和刪除的示例代碼會(huì)存在一個(gè)潛在的內(nèi)存泄漏問題:一些具有值的元素仍被切片a引用,因此無法被垃圾回收機(jī)制回收掉。下面的代碼可以解決這個(gè)問題。

          剪切

          copy(a[i:], a[j:])
          for k, n := len(a)-j+i, len(a); k < n; k++ {
           a[k] = nil // 或類型T的零值
          }
          a = a[:len(a)-j+i]

          刪除

          copy(a[i:], a[i+1:])
          a[len(a)-1] = nil // 或類型T的零值
          a = a[:len(a)-1]

          刪除但不保留元素原有順序

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

          內(nèi)部擴(kuò)張

          在切片a的索引i之后擴(kuò)張j個(gè)元素。

          使用兩個(gè)append函數(shù)完成,即先將索引i之后的元素追加到一個(gè)長(zhǎng)度為j的切片后,再將這個(gè)切片中的所有元素追加到切片a的索引i之后。

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

          擴(kuò)張的這一部分元素為T類型的零值。

          尾部擴(kuò)張

          將切片a的尾部擴(kuò)張j個(gè)元素的空間。

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

          擴(kuò)張的這一部分元素同樣為T類型的零值。

          過濾

          按照一定的規(guī)則將切片a中的元素進(jìn)行就地過濾。

          這里假設(shè)過濾的條件已封裝為keep函數(shù),使用for range遍歷切片a的所有元素逐一調(diào)用keep函數(shù)進(jìn)行過濾。

          n := 0
          for _, x := range a {
           if keep(x) {
            a[n] = x  // 保留該元素
            n++
           }
          }
          a = a[:n]  // 截取切片中需保留的元素

          插入

          將元素x插入切片a的索引i處。

          還是使用兩個(gè)append函數(shù)完成插入x的操作。

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

          第二個(gè)append函數(shù)創(chuàng)建了一個(gè)具有自己底層數(shù)組的新切片,并將a[i:]中的元素復(fù)制到該切片,然后由第一個(gè)append函數(shù)將這些元素復(fù)制回切片a。

          我們可以通過使用另一種方法來避免新切片的創(chuàng)建(以及由此產(chǎn)生的內(nèi)存垃圾)和第二個(gè)副本:

          a = append(a, 0 /* 這里應(yīng)使用元素類型的零值 */)
          copy(a[i+1:], a[i:])
          a[i] = x

          追加

          將元素x追加到切片a的最后。

          這里使用append函數(shù)即可。

          a = append(a, x)

          彈出

          將切片a的最后一個(gè)元素彈出。

          這里使用切片表達(dá)式完成彈出操作。

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

          彈出切片a的第一個(gè)元素。

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

          前插

          將元素x前插到切片a的開始。

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

          其他技巧

          過濾而不分配

          此技巧使用了一個(gè)事實(shí),即切片b與原始切片a共享相同的底層數(shù)組和容量,因此存儲(chǔ)已重新用于過濾的切片。當(dāng)然,原始內(nèi)容被修改了。

          b := a[:0]
          for _, x := range a {
           if f(x) {
            b = append(b, x)
           }
          }

          對(duì)于必須被垃圾回收的元素,在完成上述操作后可以添加以下代碼:

          for i := len(b); i < len(a); i++ {
           a[i] = nil // 或T類型的零值
          }

          翻轉(zhuǎn)

          將切片a的元素順序翻轉(zhuǎn)。

          通過迭代兩兩互換元素完成。

          for i := len(a)/2-1; i >= 0; i-- {
           opp := len(a)-1-i
           a[i], a[opp] = a[opp], a[i]
          }

          同樣的操作:

          for left, right := 0len(a)-1; left < right; left, right = left+1, right-1 {
           a[left], a[right] = a[right], a[left]
          }

          打亂順序

          打亂切片a中元素的順序。

          Fisher–Yates算法:

          for i := len(a) - 1; i > 0; i-- {
              j := rand.Intn(i + 1)
              a[i], a[j] = a[j], a[i]
          }

          從go1.10開始,可以使用math/rand.Shuffle。

          rand.Shuffle(len(a), func(i, j int) {
           a[i], a[j] = a[j], a[i]
          })

          使用最小分配進(jìn)行批處理

          如果你想對(duì)一個(gè)大型切片a的元素分批進(jìn)行處理,這會(huì)很有用。

          actions := []int{0123456789}
          batchSize := 3
          batches := make([][]int0, (len(actions) + batchSize - 1) / batchSize)

          for batchSize < len(actions) {
              actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize])
          }
          batches = append(batches, actions)

          得到的效果如下:

          [[0 1 2] [3 4 5] [6 7 8] [9]]

          就地刪除重復(fù)元素(元素可比較)

          import "sort"

          in := []int{3,2,1,4,3,2,1,4,1// 切片元素可以是任意可排序的類型
          sort.Ints(in)
          j := 0
          for i := 1; i < len(in); i++ {
           if in[j] == in[i] {
            continue
           }
           j++
           // 需要保存原始數(shù)據(jù)時(shí)
           // in[i], in[j] = in[j], in[i]
           // 只需要保存需要的數(shù)據(jù)時(shí)
           in[j] = in[i]
          }
          result := in[:j+1]
          fmt.Println(result) // [1 2 3 4]

          元素存在就移到最前面,不存在則放在前面

          // moveToFront 把needle移動(dòng)或添加到haystack的前面
          func moveToFront(needle string, haystack []string) []string {
           if len(haystack) != 0 && haystack[0] == needle {
            return haystack
           }
           prev := needle
           for i, elem := range haystack {
            switch {
            case i == 0:
             haystack[0] = needle
             prev = elem
            case elem == needle:
             haystack[i] = prev
             return haystack
            default:
             haystack[i] = prev
             prev = elem
            }
           }
           return append(haystack, prev)
          }

          haystack := []string{"a""b""c""d""e"// [a b c d e]
          haystack = moveToFront("c", haystack)         // [c a b d e]
          haystack = moveToFront("f", haystack)         // [f c a b d e]

          滑動(dòng)窗口

          將切片input生成size大小的滑動(dòng)窗口。

          func slidingWindow(size int, input []int) [][]int {
           // 返回入?yún)⒌那衅鳛榈谝粋€(gè)元素
           if len(input) <= size {
            return [][]int{input}
           }

           // 以所需的精確大小分配切片
           r := make([][]int0len(input)-size+1)

           for i, j := 0, size; j <= len(input); i, j = i+1, j+1 {
            r = append(r, input[i:j])
           }

           return r
          }

          示例:

          a := []int{12345}
          res := slidingWindow(2, a)
          fmt.Println(res)

          輸出:

          [[1 2] [2 3] [3 4] [4 5]]

          參考資料:https://github.com/golang/go/wiki/SliceTricks



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 17
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久热精品视频在线观看 | 呦小泬哟小泬哟小泬免费看 | 国内精品在线播放 | 图片区小说区视频区91 | 99在线精品视频观看 |