这很容易在Java中完成-哈希码似乎是指向对象或其他内容的指针。为什么不迅速为我们提供相同的舒适感,却要求我们自己定义功能?
答案 0 :(得分:2)
哈希码似乎是指向对象或某物的指针。
这确实是描述Java中hashCode()
(Object#hashCode()
)的默认实现。
Java提供hashCode()
的默认实现这一事实引起了我所遇到的数百个错误。
在Java中,hashCode()
和equals()
必须保持一致才能与基于哈希值的集合(例如HashMap
或HashSet
)一起使用。
在许多许多类中,equals()
的定义不同于将指针与对象或某物进行比较。 hashCode()
的默认实现与这样的equals()
永远不会保持一致,并且会导致某些错误,这些错误很难找到。
Swift试图告诉我们,散列值必须以比其他语言更强的方式与类型的平等保持一致。
SE-0185引入了一种简便的方法来符合Equatable
和Hashable
。在合适的条件下,相等很小,您不需要定义函数。
Java中hashCode()
的默认实现是没有用的,并且在覆盖equals()
时必须覆盖它,并且即使我们忘记覆盖hashCode()
,编译器也不会发出警告。这真的是一种安慰吗?
答案 1 :(得分:1)
Java唯一的值类型是固定的“原语”集(bool
,char
,byte
,short
,int
,{{1} },long
,float
)。 Swift具有创建Java不会创建的值类型的通用机制。鉴于值类型没有身份,使用身份(实例地址)作为double
默认值的基础是没有意义的。
通过复制指向对象的指针来传递引用类型(类的实例,称为对象)。如果您有某个对象hashValue
,使用其默认值a
,然后将其作为参数hashCode
传递给函数f
,则p
将与p.hashCode()
,因为保留了指针值。
值类型(结构,元组和枚举的实例)通过复制其内容或它们的a.hashCode()
来传递。如果您有某个实例value
,并尝试根据a
成员开始的基地址派生一个hashCode
,那么您将获得一些价值。但是,当您将其作为参数a
传递给函数f
时,将导致p
成员的副本进入堆栈中为a
预留的内存中在p
之内。与f
在不同的位置。如果您尝试根据a
成员开始的基址来派生hashCode
,您将得到一个不同的值!
因此,值类型不存在身份。这里的模式为p
的{{1}}与在其他地方也是Int8
的{{1}}模式具有完全相同的值。这不同于两个堆对象,每个堆对象可以具有相同的内容,但是通过它们不同的(但是伪永久性的)位置来区分,这构成了它们的身份基础。
有一个开放的JDK enhancement proposal (#169)用于引入通用值类型,因为它在减少0b00000001
,Int8
等小型短期对象的数量上具有非常明显的性能优势。我怀疑他们会遇到Swift遇到的相同障碍。这是JEP(重点是我的)的摘录:
摘要
提供JVM基础架构,以处理不可变的 无参考对象,支持有效的价值计算 具有非原始类型。
...
说明
将永久定义一个新的操作员锁,该操作员将使用一个对象 并将其标记为不变且无别名。
通常,永久锁定的对象无法承受 对于依赖引用标识的任何操作都有意义 对象。如果操作取决于引用标识, 根据是否适用,运算会产生不同的结果 到原始对象或其克隆之一。因此:
- 字段和数组元素无法更改。
- 不能进行同步 执行。
- 无法调用等待或通知的方法。
- 无法查询身份哈希码。
- 不应执行指针相等性检查。
答案 2 :(得分:0)
我发布了另一个答案here on StackOverflow,该答案涵盖了如何为所有类类型隐式添加Equatable
,而无需通过简单地为==
和{{ 1}}。
此外,我展示了如何通过简单地在要创建的类型上!=
指定协议,而不必实现散列函数,也可以使所有类都实现Hashable
。它使用扩展为您实现。
两者都已成为我创建的所有新项目的主要内容。
HTH!