首頁 >後端開發 >Golang >如何在 Go 中有效率地從切片中刪除元素?

如何在 Go 中有效率地從切片中刪除元素?

PHPz
PHPz轉載
2024-02-08 22:03:22740瀏覽

如何在 Go 中高效地从切片中删除元素?

php小編蘋果為您介紹如何在 Go 中高效地從切片中刪除元素。在 Go 語言中,刪除切片中的元素是一個常見的操作,但是由於切片的特性,直接刪除一個元素可能會導致切片長度的改變,從而影響後續的操作。為了有效率地刪除切片中的元素,我們可以利用切片的特性和一些內建函數來實現。以下將為您詳細介紹幾種常用的方法。

問題內容

有多種方法可以刪除切片元素。但是,如果我有一個需要大量處理切片的應用程式怎麼辦? Go 切片對於添加新元素進行了很好的優化,但是有沒有一種有效的方法可以從切片中刪除元素(不僅是速度,而且還優化了內存)。

我知道 Go 1.21 中引入的 slices.Delete 函數,但在幕後它使用了以下眾所周知的技術:

return append(s[:i], s[j:]...)

看起來在這種情況下底層數組不會減少。這對速度很有好處,但如果我們有很多元素(例如 100k 或 1M),然後將它們減少到很少(例如只有 10 個),該怎麼辦?看起來沒有像用於增加切片容量的記憶體優化那樣的記憶體優化。

當我們不需要保留切片中元素的順序時,可以使用以下方法(前往遊樂場連結):

func sliceDel[S ~[]E, E any](s S, i, j int) S {
    lastIdx := len(s) - (j - i)
    copy(s[i:], s[lastIdx:])
    return s[:lastIdx]
}

當我們有大切片和少量要刪除的元素時,這會很有用(背後的想法是複製少量切片元素)。

關於內存,兩種情況下容量都是相同的並且不會減少。例如:

// Reduce slice almost to zero
    for i := 0; i < sliceSize/2-1; i++ {
        sl = sliceDel(sl, 0, 2)
    }
    fmt.Printf("len = %d, cap = %d", len(sl), cap(sl))
        // Output: len = 2, cap = 100000

    // Reduce slice almost to zero
    for i := 0; i < sliceSize/2-1; i++ {
        sl = slices.Delete(sl, 0, 2)
    }
    fmt.Printf("len = %d, cap = %d", len(sl), cap(sl))
        // Output: len = 2, cap = 100000

那麼,有沒有辦法優化記憶體使用呢?例如,如果切片的長度小於其容量的一半,則將容量減少一半。

我也想知道如何有效地做到這一點,例如這樣的技術s[:len(s):len(s)] (完整切片表達式由slices.Clip 使用)不會減少底層數組- 它僅在切片結構中保存新容量,以避免在將新元素附加到子切片時重寫父切片元素(正如本提案中提到的)。

解決方法

不存在「一般最佳」解決方案。您在問題中展示了多種方法,對於特定場景,每種方法可能比其他方法更好。

如果您遇到這樣的情況,當您想要保留許多元素中的少數元素時,甚至不要開始刪除這些元素。用這幾個元素建立一個新切片。除了速度更快之外,這肯定也解決了記憶體問題。

除了分配和使用新切片之外,您無法透過使用完整切片表達式來減少記憶體使用量。只要存在對後備數組的引用,它就不會縮小(至少在當前的 Go 版本中不會)。如果您遇到分配了大後備數組但只使用其中一小部分的情況,則可以分配一個新切片並手動複製元素,以讓大數組被垃圾收集。

還要考慮到,如果您有一個很大的切片,您可能需要從中刪除許多元素,那麼切片可能不是最好的資料結構。例如,您可以嘗試使用鍊錶,或者甚至可以嘗試映射:從鍊錶或映射中刪除元素會快得多,映射還將提供快速(O(n)) 查找時間,如下所示好吧。

以上是如何在 Go 中有效率地從切片中刪除元素?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除