Golang中json.Unmarshal遇到容量过高的问题

Golang中json.Unmarshal遇到容量过高的问题 我注意到 json.Unmarshal 有一个奇怪的行为。你可以在这里查看代码:

func main() {
    fmt.Println("hello world")
}

总结:我将一个单元素列表解组到 []string 中。之后结果的长度为1,这很正常,但容量为4,其他元素都是空的。

有人知道为什么会发生这种情况吗?

4 回复

在第12行的操作会导致元素为空:在重新切片之前这些元素是不可访问的。 如果对切片进行重新切片,会创建一个具有不同长度的新切片,因此会出现空元素的结果。

确实——你说得对

但在上面的注释中提到:

// 要将JSON数组解组到切片中,Unmarshal会将切片长度重置为零,然后将每个元素追加到切片中。

更多关于Golang中json.Unmarshal遇到容量过高的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的,确实如此。它说的是重置切片的长度,而不是容量。

这句话只是意味着 Unmarshal 会覆盖已使用切片中的任何条目。

例如,如果它写入 2 个条目,但之前已有 3 个条目,那么第三个条目将变得不可见(并被垃圾回收)。

容量是另一个不同的属性,它仅表示切片正在使用以及无需重新分配和复制即可使用的内存量。

答案就在代码中:https://golang.org/src/encoding/json/decode.go?s=4051:4099#L560

我不认为这是奇怪的行为,长度为1,容量为4,这符合预期。

基本上:容量为4只是一个"优化",因为假设大多数数组都大于1,这样可以避免昂贵的重新分配操作。

在第12行执行的操作会导致元素为空:在重新切片之前这些元素是不可访问的。

如果对切片进行重新切片,会创建一个具有不同长度的新切片,因此会得到空元素的结果。

func main() {
    fmt.Println("hello world")
}

在 Go 语言中,json.Unmarshal 使用 slice 进行解码时,确实可能会出现容量高于实际长度的现象。这是因为 Go 的 encoding/json 包在解码 JSON 数组到切片时,为了优化性能,可能会预先分配一个比实际元素数量更大的底层数组容量。这类似于使用 make 创建切片时指定容量,以减少多次追加时的内存重新分配。

在你的例子中,虽然 JSON 数组只有一个元素,但解码后的 []string 切片容量为 4,这是正常的。Go 运行时可能根据内部策略分配了额外的容量,但不会影响切片的逻辑长度(len),只有长度内的元素是有效的,其余容量部分为空或未初始化,不应被访问。

以下是一个示例代码,演示了这种行为,并展示了如何验证长度和容量:

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	jsonData := `["singleElement"]`
	var slice []string

	err := json.Unmarshal([]byte(jsonData), &slice)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Printf("Length: %d, Capacity: %d\n", len(slice), cap(slice))
	fmt.Printf("Slice content: %v\n", slice)
}

运行此代码,输出可能类似于:

Length: 1, Capacity: 4
Slice content: [singleElement]

这种行为是设计上的优化,不是错误。如果你需要精确控制容量,可以在解码后使用 copy 或重新切片来调整容量,例如 slice = slice[:len(slice):len(slice)] 以将容量设置为与长度相同。但在大多数情况下,不需要额外处理,因为容量不影响切片的逻辑使用。

回到顶部