如何解决如何修改切片中结构的字段? 通过其索引引用集合元素将指针保留在切片中
我有一个名为test.json
的JSON文件,其中包含:
[
{
"name" : "john","interests" : ["hockey","jockey"]
},{
"name" : "lima","interests" : ["eating","poker"]
}
]
现在,我编写了一个golang脚本,该脚本将JSON文件读取到一个结构片段,然后在条件检查后,通过遍历该片段来修改结构字段。
这是我到目前为止尝试过的:
package main
import (
"log"
"strings"
"io/ioutil"
"encoding/json"
)
type subDB struct {
Name string `json:"name"`
Interests []string `json:"interests"`
}
var dbUpdate []subDB
func getJSON() {
// open the file
filename := "test.json"
val,err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
err = json.Unmarshal(val,&dbUpdate)
}
func (v *subDB) Change(newresponse []string) {
v.Interests = newresponse
}
func updater(name string,newinterest string) {
// iterating over the slice of structs
for _,item := range dbUpdate {
// checking if name supplied matches to the current struct
if strings.Contains(item.Name,name) {
flag := false // declare a flag variable
// item.Interests is a slice,so we iterate over it
for _,intr := range item.Interests {
// check if newinterest is within any one of slice value
if strings.Contains(intr,newinterest) {
flag = true
break // if we find one,we terminate the loop
}
}
// if flag is false,then we change the Interests field
// of the current struct
if !flag {
// Interests holds a slice of strings
item.Change([]string{newinterest}) // passing a slice of string
}
}
}
}
func main() {
getJSON()
updater("lima","jogging")
log.Printf("%+v\n",dbUpdate)
}
我得到的输出是:
[{Name:john Interests:[hockey jockey]} {Name:lima Interests:[eating poker]}]
但是我应该得到如下输出:
[{Name:john Interests:[hockey jockey]} {Name:lima Interests:[jogging]}]
我不太确定自己在这里做错了什么,因此如果有人向我指出相同的内容,我将非常负责。我的理解是,由于Change()
传递了一个指针,因此应该直接修改该字段。
解决方法
问题
让我们列举一下语言规范says on the for ... range
loops:
带有“ range”子句的“ for”语句遍历所有条目 数组,切片,字符串或映射的形式,或通道上接收的值。 对于每个条目,它将 iteration值分配给相应的 iteration 变量(如果存在),然后执行该块。
所以,在
for _,item := range dbUpdate { ... }
整个语句构成一个范围,在该范围中声明了名为item
的变量,并为该变量分配了{em> dbUpdate
每个元素的值依次形成第一个到最后一个-语句执行其迭代。
Go中的所有赋值,无论何时何地,都会将要赋值的表达式的值复制到接收该值的变量中。
所以,当你拥有
type subDB struct {
Name string `json:"name"`
Interests []string `json:"interests"`
}
var dbUpdate []subDB
您有一个切片,其切片的支持数组包含一组元素,每个元素的类型均为subDB
。
因此,当for ... range
遍历您的切片时,在每次迭代中,将对当前切片元素中包含的subDB
值的字段进行浅表复制:将这些字段的值复制到变量{ {1}}。
我们可以这样重写一下:
item
如您所见,如果在循环的主体中对for i := 0; i < len(dbUpdate); i++ {
var item subDB
item = dbUpdate[i]
...
}
进行了突变,则对其所做的更改不会以任何方式影响当前正在迭代的集合的元素。
解决方案
广义上讲,解决方案是完全了解Go在它实现的大多数内容中都非常简单的事实,因此item
并不是魔术:迭代变量只是一个变量,并且分配只是一个分配。
关于解决特定问题,有多种方法。
通过其索引引用集合元素
做
range
一个推论是,有时,当元素很复杂或者您想将其变异委托给某个函数时,您可以使用指向它的指针:
for i := range dbUpdate {
dbUpdate[i].FieldName = value
}
将指针保留在切片中
如果切片像#p>
for i := range dbUpdate {
p := &dbUpdate[i]
mutateSubDB(p)
}
...
func mutateSubDB(p *subDB) {
p.SomeField = someValue
}
...并且您将保留指向(通常是堆分配的)var dbUpdates []*subDB
值的指针,
SubDB
由于切片包含指针,因此语句自然会将指向SubDB(匿名)变量的指针复制到for _,ptr := range dbUpdate { ... }
中,因此赋值将复制指针。
由于所有包含相同地址的指针都指向相同的值,因此通过保留在迭代变量中的指针来使目标变量发生变异将使切片元素所指向的事物也发生变异。
选择哪种方法通常应取决于考虑因素 ,而不是考虑如何对元素进行迭代-仅仅因为一旦理解了代码为什么不起作用,就不再有此问题了
通常:如果您的值确实很大,请考虑保留指向它们的指针。 如果需要同时从多个位置引用值,请保留指向它们的指针。在其他情况下,直接保留值-可以大大改善CPU数据cache locality(简而言之,到您要访问下一个元素时,其内容很可能已经从内存中获取了,而实际上并没有当CPU必须追逐一个指针以通过它访问某个任意内存位置时发生。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。