【发布时间】:2019-01-28 07:08:21
【问题描述】:
我来自 Python/Ruby/JavaScript 背景。我了解指针的工作原理,但是,我不完全确定如何在以下情况下利用它们。
假设我们有一个虚构的 Web API,它搜索某个图像数据库并返回一个 JSON 描述在找到的每个图像中显示的内容:
[
{
"url": "https://c8.staticflickr.com/4/3707/11603200203_87810ddb43_o.jpg",
"description": "Ocean islands",
"tags": [
{"name":"ocean", "rank":1},
{"name":"water", "rank":2},
{"name":"blue", "rank":3},
{"name":"forest", "rank":4}
]
},
...
{
"url": "https://c3.staticflickr.com/1/48/164626048_edeca27ed7_o.jpg",
"description": "Bridge over river",
"tags": [
{"name":"bridge", "rank":1},
{"name":"river", "rank":2},
{"name":"water", "rank":3},
{"name":"forest", "rank":4}
]
}
]
我的目标是在 Go 中创建一个数据结构,它将每个标签映射到如下所示的图像 URL 列表:
{
"ocean": [
"https://c8.staticflickr.com/4/3707/11603200203_87810ddb43_o.jpg"
],
"water": [
"https://c8.staticflickr.com/4/3707/11603200203_87810ddb43_o.jpg",
"https://c3.staticflickr.com/1/48/164626048_edeca27ed7_o.jpg"
],
"blue": [
"https://c8.staticflickr.com/4/3707/11603200203_87810ddb43_o.jpg"
],
"forest":[
"https://c8.staticflickr.com/4/3707/11603200203_87810ddb43_o.jpg",
"https://c3.staticflickr.com/1/48/164626048_edeca27ed7_o.jpg"
],
"bridge": [
"https://c3.staticflickr.com/1/48/164626048_edeca27ed7_o.jpg"
],
"river":[
"https://c3.staticflickr.com/1/48/164626048_edeca27ed7_o.jpg"
]
}
如您所见,每个图片 URL 可以同时属于多个标签。如果我有数千张图像甚至更多标签,如果图像 URL 字符串按每个标签的值复制,则此数据结构可能会变得非常大。这就是我想利用指针的地方。
我可以用 Go 中的两个结构来表示 JSON API 响应,func searchImages() 模仿了假 API:
package main
import "fmt"
type Image struct {
URL string
Description string
Tags []*Tag
}
type Tag struct {
Name string
Rank int
}
// this function mimics json.NewDecoder(resp.Body).Decode(&parsedJSON)
func searchImages() []*Image {
parsedJSON := []*Image{
&Image {
URL: "https://c8.staticflickr.com/4/3707/11603200203_87810ddb43_o.jpg",
Description: "Ocean islands",
Tags: []*Tag{
&Tag{"ocean", 1},
&Tag{"water", 2},
&Tag{"blue", 3},
&Tag{"forest", 4},
},
},
&Image {
URL: "https://c3.staticflickr.com/1/48/164626048_edeca27ed7_o.jpg",
Description: "Bridge over river",
Tags: []*Tag{
&Tag{"bridge", 1},
&Tag{"river", 2},
&Tag{"water", 3},
&Tag{"forest", 4},
},
},
}
return parsedJSON
}
现在,导致内存中数据结构非常大的次优映射函数可能如下所示:
func main() {
result := searchImages()
tagToUrlMap := make(map[string][]string)
for _, image := range result {
for _, tag := range image.Tags {
// fmt.Println(image.URL, tag.Name)
tagToUrlMap[tag.Name] = append(tagToUrlMap[tag.Name], image.URL)
}
}
fmt.Println(tagToUrlMap)
}
我可以修改它以使用指向 Image struct URL 字段的指针,而不是按值复制它:
// Version 1
tagToUrlMap := make(map[string][]*string)
for _, image := range result {
for _, tag := range image.Tags {
// fmt.Println(image.URL, tag.Name)
tagToUrlMap[tag.Name] = append(tagToUrlMap[tag.Name], &image.URL)
}
}
它有效,我的第一个问题是,在我以这种方式构建映射后,result 数据结构会发生什么? Image URL 字符串字段是否会以某种方式留在内存中,而 result 的其余部分将被垃圾回收?或者result 数据结构是否会因为指向它的成员而保留在内存中直到程序结束?
另一种方法是将 URL 复制到中间变量并使用指向它的指针:
// Version 2
tagToUrlMap := make(map[string][]*string)
for _, image := range result {
imageUrl = image.URL
for _, tag := range image.Tags {
// fmt.Println(image.URL, tag.Name)
tagToUrlMap[tag.Name] = append(tagToUrlMap[tag.Name], &imageUrl)
}
}
这样更好吗? result 数据结构会被正确地垃圾回收吗?
或者我应该在 Image 结构中使用指向字符串的指针?
type Image struct {
URL *string
Description string
Tags []*Tag
}
有没有更好的方法来做到这一点?我也很感激 Go 上的任何资源,这些资源深入描述了指针的各种用途。谢谢!
https://play.golang.org/p/VcKWUYLIpH7
更新:我担心最佳内存消耗和不会产生最不需要的垃圾。我的目标是尽可能使用最少的内存。
【问题讨论】:
-
这篇文章应该会有所帮助。 dave.cheney.net/2017/04/26/…。 Golang 使用指针复制。所以结果可以在循环之后被垃圾回收。
-
我喜欢你的问题,因为我经常问自己同样的问题。找出答案的唯一方法是编写测试和基准测试。在几乎每个 Go 版本都优化了垃圾收集器之后,对于所有 Go 版本都没有正确的答案。如果您关心内存消耗,我会选择第 2 版。但您需要测试是否确定。
标签: string dictionary go slice memory-optimization