Golang的map不能修改元素?

烧麦
  • 212

看了很多文字说:map 元素是无法取址的, 无法对其进行修改。
比如下面例子:

package main

import "fmt"

func main() {
    m := map[int]Person{
        1: Person{"Andy", 10},
        2: Person{"Tiny", 20},
        3: Person{"Jack", 30},
    }
    fmt.Println(m[1])
    m[1].name = "KL"
    fmt.Println(m[1])
}

type Person struct {
    name string
    age  int
}

上面代码的m[1].name = "KL"会报: cannot assign to struct field m[1].name in map

我们再来看一个例子:

package main

import "fmt"

func main() {
    m := map[int]string{
        1: "A",
        2: "B",
    }
    fmt.Println(m[1])
    m[1] = "KL"
    fmt.Println(m[1])
}

type Person struct {
    name string
    age  int
}

上面代码运行正常,成功修改了map的值。

为什么2个例子有差别。map不能修改元素到底对不对呢?

回复
阅读 1.8k
4 个回答

Assignments

Each left-hand side operand must be addressable, a map index expression, or (for = assignments only) the blank identifier.

map indexing expression 也是可以被赋值的(虽然它不可以取址)。

m[1] 是 map indexing expression ,但 m[1].name 不是。

那个,@fefe 说得对。具体的来看一下我写的这个例子:

package main

import "fmt"

func main() {
    m := map[int]Person{
        1: Person{"Andy", 10},
        2: Person{"Tiny", 20},
        3: Person{"Jack", 30},
    }
    fmt.Println(m[1])
    //fmt.Printf("%p\n",&m[1]) // map中的值是无法取地址的,这里会报错:cannot take the address of m[1]
    t := m[1] // 无法直接对map里的值取地址,那么没法直接修改,那么把这个值拷贝一份给t
    fmt.Printf("%p\n",&t)
    t.name = "test" // 修改t的name值
    fmt.Println(t)
    fmt.Println(m)
    m[1] = t // 然后把t传递给m[1],修改m[1]位置的值
    fmt.Println(m)
}

type Person struct {
    name string
    age  int
}

输出

{Andy 10}
0xc000114078
{test 10}
map[1:{Andy 10} 2:{Tiny 20} 3:{Jack 30}]
map[1:{test 10} 2:{Tiny 20} 3:{Jack 30}]

也就是说m[1]里对应的value,只能通过m[1]进行访问,这个m[1]对应的值是一个完整的整体,而对于m这个map来说,你修改m[1]是可以的,从另外一个角度来说,你修改m[1].name不属于map的语法操作了,相当于来说你操作m[1]里存的数据和map的操作无关,这么理解就行了。

然后你拷贝一份m[1]数据到t上,然后你操作t就是操作对应的type Person struct类型,与map无关,然后你再进行m[1]=t,这个语法符合map语法操作,那么自然是成功的,从另外一个角度想,golangmap操作里面的值,编译推断没有那么智能,这么想就行了,不需要纠结那么多。

再多聊几句,m[1].name="test"这种形式,从逻辑上来说,先取m这个map里m[1]找到m[1].name进行修改。而我上面写的,从逻辑上来说,先取m这个map里找到m[1],拷贝一份给t,然后对t.name进行修改,然后再把t放回m[1]。这整个操作都是操作,要么就是按照map的语法操作map,要么就是按照struct的语法操作struct,根本不会涉及到一起操作。

大概如此这般,你要是还不理解我也没办法。

如果里面是结构体的话得通过引用去修改, 不是引用的话相当于值拷贝实例后的结构体数据到map里面, 多用用指针还是很方便的

type test struct {
    Name string
}

func (t test) String() string {
    b, _ := json.MarshalIndent(t, "", "\t")
    return string(b)
}

fun main(){
    m := map[int]*test{
        1: &test{Name: "111"},
    }
    m[1].Name = "222"
    fmt.Println(m)
}

// map[1:{
//        "Name": "222"
// }]
已注销
  • 2
新手上路,请多包涵

m[1].name = "KL" 语法不对。。。。
如果你想修改map中的元素,将map中person类型改成指针即可

func TestUpdateMap(t *testing.T) {
    m := map[int]*Person{
        1: &Person{"Andy", 10},
        2: &Person{"Tiny", 20},
        3: &Person{"Jack", 30},
    }
    fmt.Println(m[1])
    
    p := m[1]
    p.name = "KL"
    fmt.Println(m[1])
}

type Person struct {
    name string
    age  int
}
宣传栏