使用用户定义的键和用户定义的相等性进行 Go map 的操作吗?

huangapple go评论78阅读模式
英文:

Go map with user-defined key with user-defined equality?

问题

假设我在Go语言中有一个结构体类型,我想将其用作映射中的键,但我不想使用Go语言的内置相等操作。构建这样一个映射的最佳方法是什么?

以一个具体的例子来说明,这是我的键类型和相等操作:

type Key struct {
    a *int
}

func Equal(x Key, y Key) bool {
    return *x.a == *y.a
}

我该如何构建一个使用Equal进行键比较的映射?

英文:

Suppose I have a struct type in Go that I want to use as a key in a map, but I don't want to use Go's builtin equality operation. What's the best way to build such a map?

For a concrete example, here is my key type and equality operation:

type Key struct {
    a *int
}

func Equal(x Key, y Key) bool {
    return *x.a == *y.a
}

How can I build a map that uses Equal for key comparison?

答案1

得分: 28

Go语言对于用作map键的值具有严格的可比较语义。因此,与许多其他语言不同,你不能为map键定义自己的哈希码和相等性函数。

然而,可以考虑以下解决方法。不直接使用结构体实例作为键,而是使用结构体的派生属性作为键,该属性本身可用作键,并具有所需的相等性语义。通常,可以简单地派生一个整数或字符串值作为哈希码,作为实例的标识。

重要的是,只有在键真正表示所存储值的语义标识时,它们才会发生碰撞。也就是说,相应的值应该是真正可互换的。

例如:

type Key struct {
  a *int
}

func (k *Key) HashKey() int {
  return *(*k).a
}

k1, k2 := Key{intPtr(1)}, Key{intPtr(2)}
m := map[int]string{}
m[k1.HashKey()] = "one"
m[k2.HashKey()] = "two"
// m = map[int]string{1:"one", 2:"two"}
m[k1.HashKey()] // => "one"

当然,不可变性是这种方法的一个关键问题。在上面的示例中,如果修改了字段a,那么该实例就不能再用作哈希键,因为其标识已经改变。

英文:

Go has strict comparable semantics for values used as map keys. As such, you cannot define your own hash code and equality functions for map keys as you can in many other languages.

However, consider the following workaround. Instead of using the struct instances directly as a keys, use a derived attribute of the struct which is intrinsically usable as a key and has the equality semantics you desire. Often it is simple to derive an integer or string value as a hash code which serves as the identity for an instance.

Importantly, the keys should only collide if they truly represent semantic identity of the value being stored. That is, corresponding values should be truly interchangeable.

For example:

type Key struct {
  a *int
}

func (k *Key) HashKey() int {
  return *(*k).a
}

k1, k2 := Key{intPtr(1)}, Key{intPtr(2)}
m := map[int]string{}
m[k1.HashKey()] = "one"
m[k2.HashKey()] = "two"
// m = map[int]string{1:"one", 2:"two"}
m[k1.HashKey()] // => "one"

Of course, immutability is a critical concern with this approach. In the example above, if you modify the field a then the instance can no longer be used as a hash key because its identity has changed.

答案2

得分: 9

这在Go语言中是不可能的。Go语言没有运算符重载或者可以重写的“Equality”方法(因为它不像.NET那样继承自一个共同的基类,你的例子让我想起了这一点)。如果你对相等比较感兴趣,可以参考这个回答提供的更多信息:https://stackoverflow.com/questions/20309751/is-it-possible-to-define-equality-for-named-types-structs

正如评论中提到的,如果你想让类似这样的功能工作,我建议在对象上使用一个属性作为键。你可以根据如何设置该属性的值来定义相等性(例如,它可以是对象字节的校验和之类的,如果你想要成员逐个比较)。

英文:

This is not possible in Go. There is no operator overloading or 'Equality' method you can override (due to not inheriting from a common base class like in .NET which your example reminds me of). This answer has more information on equality comparisons if you're interested; https://stackoverflow.com/questions/20309751/is-it-possible-to-define-equality-for-named-types-structs

As mentioned in the comments if you want to make something like this work I would recommend using a property on the object as a key. You can define equality based on how you set the value of that property (like it could be a checksum of the objects bytes or something if you're looking for memberwise equality).

huangapple
  • 本文由 发表于 2015年4月16日 06:25:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/29662003.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定