You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vala中Gee.Map存储结构体后无法取值?问题排查

问题:Gee.Map存储结构体实例无法检索对应值的原因

我编写了如下示例代码:一个存储Foo类实例的Gee.Map,以及一个存储Bar结构体实例的Gee.Map:

public class Maps {
    Gee.Map<Foo?, int> map1 = new Gee.HashMap<Foo?, int> ();
    Gee.Map<Bar?, int> map2 = new Gee.HashMap<Bar?, int> ();
     
    public Maps () {
        Foo a = new Foo ();
        Foo b = new Foo ();
        Foo c = new Foo ();
        map1.set (a, 1);
        map1.set (b, 2);
        map1.set (c, 3);
        stdout.printf ("Foo = %d %d %d %d\n", map1.get (a), map1.get (b), map1.get (c), map1.size);

        Bar e = Bar ();
        Bar d = Bar ();
        Bar f = Bar ();
        map2.set (d, 1);
        map2.set (e, 2);
        map2.set (f, 3);
        stdout.printf ("Bar = %d %d %d %d\n", map2.get (d), map2.get (e), map2.get (f), map2.size);
    }
}

public class Foo {
    public string name;
    public Foo () {
        name = "foo";
    }
}

public struct Bar {
    public string name;
    public Bar () {
        name = "bar";
    }
}

public static int main (string[] args) {
    stdout.printf ("Start\n");
    var a = new Maps ();
    stdout.printf ("End\n");
    return 0;
}

程序输出如下:

Start
Foo = 1 2 3 3
Bar = 0 0 0 3
End

我无法从map2中检索到对应的值,请问操作中存在哪些错误?


错误分析与解决方案

核心原因:值类型与引用类型的语义差异

Vala中类(class)是引用类型,结构体(struct)是值类型,二者在Gee.HashMap中的行为完全不同:

  • Foo是类,变量a/b/c持有的是堆上实例的引用。Gee.HashMap默认使用引用的内存地址计算哈希值,并用引用相等性判断键是否匹配。存入和取出用的是同一个引用,因此能正确匹配到对应值。
  • Bar是结构体,属于值类型,变量d/e/f存储的是结构体的完整值。当调用map2.set(d, 1)时,HashMap会存储d的值拷贝;之后调用map2.get(d)时,传入的是d的另一个值拷贝——虽然内容相同,但这是两个独立的实例,默认的哈希计算和相等性判断基于实例身份(内存地址),因此匹配失败,返回int类型的默认值0。

解决方案

方案1:为结构体实现hash和equals方法

让HashMap基于结构体的内容而非实例身份来判断键的匹配,需要重写hash()和equals(Object)方法:

public struct Bar {
    public string name;
    public Bar () {
        name = "bar";
    }

    // 基于name字段计算哈希值
    public override int hash () {
        return name.hash ();
    }

    // 比较两个Bar实例的name字段是否相等
    public override bool equals (Object other) {
        if (!(other is Bar)) return false;
        Bar b = (Bar) other;
        return name == b.name;
    }
}

方案2:将结构体改为类

如果不需要值语义,可以把Bar改成类,和Foo一样使用引用类型,这样就能直接复用默认的引用匹配逻辑:

public class Bar {
    public string name;
    public Bar () {
        name = "bar";
    }
}

内容的提问来源于stack exchange,提问作者Wayne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 15:31:10