Go中map[string]type行为疑问:无法更新map内结构体切片
你碰到的这个情况,是Go语言里map的典型新手坑——map的value是按值传递的,说白了就是你从map里取出的那个weekContributions是原对象的副本,不是原对象本身。
为什么你的代码没生效?
看这段关键代码:
currentWeekContribution := currentActivity.yearContributions[stringWeekday] currentWeekContribution.addContribution(dayContributions{Date: d, Contribution: 1})
第一行从map里取出值的时候,Go会给你复制一个新的weekContributions结构体,你后续调用addContribution修改的都是这个副本的AllDays切片,map里原来的那个结构体完全没被碰过,所以最后打印map的时候自然还是空切片。
两种解决办法
方法1:把map的value改成指针类型(推荐)
既然修改副本没用,那我们直接让map存指针,这样取出的就是原对象的指针,修改指针指向的内容就会直接影响map里的值。
修改Activity的定义:
type Activity struct { yearContributions map[string]*weekContributions // 把value改为指针类型 }
然后更新restore方法里的初始化代码,每个值都用指针初始化:
func (currentActivity *Activity) restore() { currentActivity.yearContributions = map[string]*weekContributions{ "Sunday": {Notation: "S", AllDays: make([]dayContributions, 0)}, "Monday": {Notation: "M", AllDays: make([]dayContributions, 0)}, "Tuesday": {Notation: "T", AllDays: make([]dayContributions, 0)}, "Wednesday": {Notation: "W", AllDays: make([]dayContributions, 0)}, "Thursday": {Notation: "T", AllDays: make([]dayContributions, 0)}, "Friday": {Notation: "F", AllDays: make([]dayContributions, 0)}, "Saturday": {Notation: "S", AllDays: make([]dayContributions, 0)}, } }
这样之后,你原来的Recalibrate方法不需要改动,取出的currentWeekContribution是指针,调用addContribution修改的就是map里的原结构体,最后打印map就能看到更新后的结果了。
方法2:修改副本后重新赋值回map
如果不想改动map的value类型,那可以在修改完副本之后,把它重新放回map里,覆盖原来的值:
for d := startDate; endDate.After(d); d = d.AddDate(0, 0, 1) { stringWeekday := d.Weekday().String() currentWeekContribution := currentActivity.yearContributions[stringWeekday] currentWeekContribution.addContribution(dayContributions{Date: d, Contribution: 1}) currentActivity.yearContributions[stringWeekday] = currentWeekContribution // 把修改后的副本存回map fmt.Println(currentWeekContribution) }
这种方法是用修改后的副本替换map里原来的旧值,不过如果你的weekContributions结构体比较大,这种拷贝会有性能损耗,所以更推荐第一种指针的方式。
补充说明
Go里map的元素是不可寻址的,也就是说你不能直接写¤tActivity.yearContributions[stringWeekday]来获取原对象的地址,这也是为什么必须用上面两种方法来修改map里的结构体值。我当初刚学Go的时候也踩过这个坑,搞清楚map的value传递逻辑就好理解了。
内容的提问来源于stack exchange,提问作者Harish Ved

