私は Python/Ruby/JavaScript のバックグラウンドを持っています。ポインターがどのように機能するかは理解していますが、次の状況でそれらを活用する方法が完全にはわかりません。
画像データベースを検索し、見つかった各画像に何が表示されているかを説明する JSON を返す架空の Web API があるとします。
[
{
"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}
]
}
]
私の目標は、各タグを次のような画像 URL のリストにマップするデータ構造を Go で作成することです。
{
"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 の 2 つの構造体で 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
構造体フィールドへのポインターを使用するように変更できます。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
更新:最適なメモリ消費と、不要なガベージを最も生成しないことが心配です。私の目標は、可能な限り最小限のメモリを使用することです。