148

在 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))
}

这个对吗?

为了澄清,缓冲区被清除,以便可以重复使用。

一个例子是字节包中的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) }
4

3 回答 3

228

将切片设置为nil是清除切片的最佳方式。 nilgo中的切片表现得非常好,将切片设置为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将删除该别名。

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

于 2013-06-06T22:17:36.023 回答
141

这完全取决于您对“清晰”的定义。一个有效的当然是:

slice = slice[:0]

但有一个问题。如果切片元素的类型为 T:

var slice []T

然后通过上述“技巧”强制len(slice)为零, 不会产生任何元素

slice[:cap(slice)]

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

于 2013-06-06T20:57:20.567 回答
5

为了我自己的目的,我正在研究这个问题。我有一片结构(包括一些指针),我想确保我做对了;结束了这个线程,并想分享我的结果。

为了练习,我做了一个小操场: 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
于 2015-01-27T18:49:32.383 回答