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
相关产品推荐
相关产品推荐

