Go 没有真正的方法来缩小切片吗?这是一个问题吗?

问题描述 投票:0回答:7

我尝试 Go 一段时间了,这个问题一直困扰着我。假设我在切片中构建了一个相当大的数据集(例如,1000 万个 int64)。

package main

import (
    "math"
    "fmt"
)

func main() {
    var a []int64
    var i int64;
    upto := int64(math.Pow10(7))
    for i = 0; i < upto; i++ {
        a = append(a, i)
    }
    fmt.Println(cap(a))
}

但后来我决定我不想要其中的大部分,所以我想最终只吃其中的 10 个。我在 Go 的 wiki 上尝试过切片和删除技术,但它们似乎都没有减少切片的容量。

所以这就是我的问题:Go 是否没有真正的方法来缩小类似于

realloc()
-ing 的切片容量,其大小参数比您之前在 C 中对同一指针的调用更小?这是一个问题吗?应该如何处理?

go slice
7个回答
36
投票

实际上要执行切片的重新分配:

a = append([]T(nil), a[:newSize]...) // Thanks to @Dijkstra for pointing out the missing ellipsis.

是否将

newSize
元素复制到新的内存位置,或者是否像 realloc(3) 中那样进行实际的 in place 调整大小,完全由编译器自行决定。您可能想要调查当前状态,如果有改进的空间,也许可以提出问题

然而,这可能是一个微观优化。性能增强的第一个来源几乎总是在于选择更好的算法和/或更好的数据结构。使用一个巨大的向量最终只保留一些项目可能不是内存消耗的最佳选择。

编辑:以上仅部分正确。在一般情况下,编译器不能导出是否有其他指向切片后备数组的指针。因此重新分配不适用。上面的代码片段实际上是“保证”执行“newSize”元素的副本。对于可能造成的任何混乱,我们深表歉意。


9
投票

首先,您可以创建一个所需大小的切片并将数据复制到其中。然后垃圾收集器将释放大片。

内置复制

其次,您可以在每次希望生成大切片时重复使用它,因此您永远不会多次分配它。

最后一点,您可以使用

1e7

代替

math.Pow10(7)
    


5
投票

func main() { s := []string{"A", "B", "C", "D", "E", "F", "G", "H"} fmt.Println(s, len(s), cap(s)) // slice, length, capacity t := s[2:4] fmt.Println(t, len(t), cap(t)) u := make([]string, len(t)) copy(u, t) fmt.Println(u, len(u), cap(u)) }

它产生以下输出:

[A B C D E F G H] 8 8 [C D] 2 6 [C D] 2 2

s

是一个可容纳 8 根字符串的切片。

t
是保留部分
[C D]
的切片。
t
的长度为2,但由于它使用与
s
相同的隐藏数组,因此其容量为6(从“C”到“H”)。问题是:如何获得独立于隐藏数组
[C D]
s
切片?只需创建一个长度为 2 的新字符串切片(切片
u
)并将
t
的内容复制到
u
u
的底层隐藏数组与
s
的隐藏数组不同。

最初的问题是这样的:你有一个大切片,然后在上面创建一个新的较小切片。由于较小的切片使用相同的隐藏数组,因此垃圾收集器不会删除隐藏数组。

请参阅本文底部了解更多信息:

http://blog.golang.org/go-slices-usage-and-internals

.


3
投票
bufs 包

PS 如果您为较小的切片重新分配新内存,旧内存

可能不会

同时被释放,它将在垃圾收集器决定释放时被释放。


2
投票

slice[a:b:c]

其中返回切片的 
len

应该是

b-a
,新切片的
cap
应该是
c-a
Tips:整个过程没有复制下来,只是返回一个新的切片,该切片指向

&slice[a]

,len为

b-a
,cav为
c-a
这是你唯一要做的事:

slice= slice[0:len(slice):len(slice)];

然后切片的
cap

就会变成

len(slice) - 0
,和它的
len
一样,不进行复制。
    


0
投票

a := []int{1,2,3} fmt.Println(len(a), a) // 3 [1 2 3] a = a[:len(a)-1] fmt.Println(len(a), a) //2 [1 2]



0
投票
slices.Clip

执行相同操作。 Clip 会缩小切片,使其上限与切片长度相同 package main import ( "fmt" "math" "slices" ) func main() { var a []int64 var i int64 upto := int64(math.Pow10(3)) for i = 0; i < upto; i++ { a = append(a, i) } fmt.Println(len(a), cap(a)) a = slices.Clip(a) fmt.Println(len(a), cap(a)) }

	
© www.soinside.com 2019 - 2024. All rights reserved.