Swift为什么不隐式为每个类提供可哈希值?

时间:2018-07-17 21:15:38

标签: java swift

这很容易在Java中完成-哈希码似乎是指向对象或其他内容的指针。为什么不迅速为我们提供相同的舒适感,却要求我们自己定义功能?

3 个答案:

答案 0 :(得分:2)

哈希码似乎是指向对象或某物的指针。

这确实是描述Java中hashCode()Object#hashCode())的默认实现。

Java提供hashCode()的默认实现这一事实引起了我所遇到的数百个错误。

在Java中,hashCode()equals()必须保持一致才能与基于哈希值的集合(例如HashMapHashSet)一起使用。

在许多许多类中,equals()的定义不同于将指针与对象或某物进行比较 hashCode()的默认实现与这样的equals()永远不会保持一致,并且会导致某些错误,这些错误很难找到

Swift试图告诉我们,散列值必须以比其他语言更强的方式与类型的平等保持一致。

SE-0185引入了一种简便的方法来符合EquatableHashable。在合适的条件下,相等很小,您不需要定义函数

Java中hashCode()的默认实现是没有用的,并且在覆盖equals()时必须覆盖它,并且即使我们忘记覆盖hashCode(),编译器也不会发出警告。这真的是一种安慰吗?

答案 1 :(得分:1)

Java唯一的值类型是固定的“原语”集(boolcharbyteshortint,{{1} },longfloat)。 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)用于引入通用值类型,因为它在减少0b00000001Int8等小型短期对象的数量上具有非常明显的性能优势。我怀疑他们会遇到Swift遇到的相同障碍。这是JEP(重点是我的)的摘录:

  

摘要

     

提供JVM基础架构,以处理不可变的   无参考对象,支持有效的价值计算   具有非原始类型。

     

...

     

说明

     

将永久定义一个新的操作员锁,该操作员将使用一个对象   并将其标记为不变且无别名。

     

通常,永久锁定的对象无法承受   对于依赖引用标识的任何操作都有意义   对象。如果操作取决于引用标识,   根据是否适用,运算会产生不同的结果   到原始对象或其克隆之一。因此:

     
      
  • 字段和数组元素无法更改。
  •   
  • 不能进行同步   执行。
  •   
  • 无法调用等待或通知的方法。
  •   
  • 无法查询身份哈希码。
  •   
  • 不应执行指针相等性检查。
  •   

答案 2 :(得分:0)

我发布了另一个答案here on StackOverflow,该答案涵盖了如何为所有类类型隐式添加Equatable,而无需通过简单地为==和{{ 1}}。

此外,我展示了如何通过简单地在要创建的类型上!=指定协议,而不必实现散列函数,也可以使所有类都实现Hashable。它使用扩展为您实现。

两者都已成为我创建的所有新项目的主要内容。

HTH!