如何在Go中清除切片?


125

在Go中清除切片的适当方法是什么?

这是我在go论坛中找到的内容:

// test.go
package main

import (
    "fmt"
)

func main() {
    letters := []string{"a", "b", "c", "d"}
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
    // clear the slice
    letters = letters[:0]
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
}

这样对吗?

为了明确起见,清除了缓冲区,以便可以重用它。

一个示例是bytes包中的Buffer.Truncate函数。

注意,Reset只是调用Truncate(0)。因此看来,在这种情况下,第70行将进行评估:b.buf = b.buf [0:0]

http://golang.org/src/pkg/bytes/buffer.go

// Truncate discards all but the first n unread bytes from the buffer.
60  // It panics if n is negative or greater than the length of the buffer.
61  func (b *Buffer) Truncate(n int) {
62      b.lastRead = opInvalid
63      switch {
64      case n < 0 || n > b.Len():
65          panic("bytes.Buffer: truncation out of range")
66      case n == 0:
67          // Reuse buffer space.
68          b.off = 0
69      }
70      b.buf = b.buf[0 : b.off+n]
71  }
72  
73  // Reset resets the buffer so it has no content.
74  // b.Reset() is the same as b.Truncate(0).
75  func (b *Buffer) Reset() { b.Truncate(0) }

1
进行以下快速测试:play.golang.org/p/6Z-qDQtpbg似乎表明它可以工作(不会改变容量,但会缩短长度)
Jason Sperske

Answers:


120

这完全取决于您对“透明”的定义。有效的方法之一当然是:

slice = slice[:0]

但是有一个陷阱。如果切片元素的类型为T:

var slice []T

然后强制len(slice)为零,由上述“特技”, 使任何元件

slice[:cap(slice)]

有资格进行垃圾收集。在某些情况下,这可能是最佳方法。但这也可能是“内存泄漏”的原因-内存未使用,但有可能达到(在对“切片”进行重新切片之后),因此不会造成垃圾“可收集”。


1
有趣。还有其他方法可以从片的基础数组中删除所有元素,同时保持基础容量不变吗?
克里斯·韦伯

3
@ChrisWeber:遍历基础数组并将所有元素设置为新值
newacct 2013年

2
@jnml,我确实想重用切片(和底层数组存储),所以我不会不断分配新的切片(带有数组)。我已经编辑了我的问题,以阐明并显示标准库中的一些示例代码。
克里斯·韦伯

1
我是新手。您能否解释一下为什么这是一种最佳方法?提前致谢。
satoru

您确定切片大小重置会导致内存泄漏吗?我无法复制它
Tommaso Barbugli,

196

将切片设置为nil是清除切片的最佳方法。 nil进行中的切片表现得非常好,将切片设置为nil会将基础内存释放到垃圾收集器。

参观游乐场

package main

import (
    "fmt"
)

func dump(letters []string) {
    fmt.Println("letters = ", letters)
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
    for i := range letters {
        fmt.Println(i, letters[i])
    }
}

func main() {
    letters := []string{"a", "b", "c", "d"}
    dump(letters)
    // clear the slice
    letters = nil
    dump(letters)
    // add stuff back to it
    letters = append(letters, "e")
    dump(letters)
}

版画

letters =  [a b c d]
4
4
0 a
1 b
2 c
3 d
letters =  []
0
0
letters =  [e]
1
1
0 e

请注意,可以很容易地为切片指定别名,以便两个切片指向同一基础内存。设置为nil将删除该别名。

但是,此方法会将容量更改为零。


尼克感谢回应。请查看我的更新。我正在清除切片以供重复使用。因此,我不必希望将底层内存释放给GC,因为我只需要再次分配它即可。
克里斯·韦伯

(这是我搜索的内容!)
Timur Fayzrakhmanov 2014年

5
基于标题“如何在Go中清除切片?” 到目前为止,这是最安全的答案,应该被接受。完美的答案应该是最初接受的答案和这个答案的结合,以便人们可以自己决定。
Shadoninja 2015年

1
appendnil一直以来都在Go中发挥作用?
alediaferia's

从那以后,@ alediaferia肯定会转到1.0。
尼克·

4

我出于自己的目的考虑了一下这个问题。我有一些结构(包括一些指针),我想确保我做对了。结束了这个话题,想分享我的结果。

为了练习,我做了一些游乐场:https : //play.golang.org/p/9i4gPx3lnY

这相当于:

package main

import "fmt"

type Blah struct {
    babyKitten int
    kittenSays *string
}

func main() {
    meow := "meow"
    Blahs := []Blah{}
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{1, &meow})
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{2, &meow})
    fmt.Printf("Blahs: %v\n", Blahs)
    //fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
    Blahs = nil
    meow2 := "nyan"
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{1, &meow2})
    fmt.Printf("Blahs: %v\n", Blahs)
    fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
}

按原样运行该代码将对“ meow”和“ meow2”变量显示相同的内存地址,因为它们是相同的:

Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
Blahs: []
Blahs: [{1 0x1030e0f0}]
kittenSays: nyan

我认为可以确认该结构是垃圾回收的。奇怪的是,取消注释注释的打印行,将为喵声产生不同的内存地址:

Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
kittenSays: meow
Blahs: []
Blahs: [{1 0x1030e0f8}]
kittenSays: nyan

我认为这可能是因为打印以某种方式(?)推迟了,但是有趣的是一些内存管理行为的说明,还有一个投票赞成:

[]MyStruct = nil

不错的详细示例。谢谢!
多拉诺

2
这并不表示meo1和meow2的内存地址是相同的:0x1030e0c0不等于0x1030e0f0(前者以结束c0,后者以结束f0)。
carbocation

这里同意@carbocation,那些内存地址不一样。我不能声称能够更好地解释这里发生的事情,但这不能为我提供证据。我确实在meow2每次运行的地址中看到相同的8字节差异...
rbrtl
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.