【问题标题】:Scala: why do my case class instances have the same hashCode?Scala:为什么我的案例类实例具有相同的 hashCode?
【发布时间】:2018-07-04 14:41:21
【问题描述】:

我使用的是 Scala 2.11。

我有一个案例类 Dimension,我创建了它的 3 个实例。当我将它们放入 HashSet 时,我惊讶地发现只有 1 被正确添加。然后我尝试调试并发现它们具有相同的hashCode。

我是 Scala 新手,但在 Java 方面有很多经验。我想知道为什么即使它们具有不同的字段,它们也都具有相同的 hashCode 以及 Scala 案例类中 hashCode 方法的默认实现是什么?以及 HashSet/HashMap 在 Scala 中是如何工作的?

这是我的代码示例。

object Echo {
  def main( args:Array[String] ):Unit = {
    var d1 = new Dimension
    d1.name = "d1"
    d1.dimensionId = "1"
    println("d1:" + d1.hashCode()) // d1, d2, d3 have the same hashCode

    var d2 = new Dimension
    d2.name = "d2"
    d2.dimensionId = "2"
    println("d2:" + d2.hashCode())

    var d3 = new Dimension
    d3.name = "d3"
    d3.dimensionId = "3"
    println("d3:" + d3.hashCode())

    var l = List(d1, d2, d3)
    val categories = mutable.HashSet.empty[Dimension]

    l.foreach(md => {
      categories += md
    })

    println(categories.size) // size is 1
  }
}

case class Dimension() {
  var dimensionId: String = _
  var name: String = _
}

【问题讨论】:

    标签: scala hash hashset


    【解决方案1】:

    Scala 中的 HashCode 仅考虑案例类的构造函数中的属性。

    如果您以更实用和缩放的方式定义案例类(例如确保不变性),则行为将是预期的:

    定义

    case class Dimension(dimensionId: String, name: String)
    val d1 = Dimension("1", "d1")
    val d2 = Dimension("2", "d2")
    

    结果

      scala> println("d1:" + d1.hashCode())
        d1:732406741
        scala> println("d2:" + d2.hashCode())
        d2:952021182
    

    你可以在这个很棒的答案here中找到 hashcode 方法的生成代码

    【讨论】:

      【解决方案2】:

      引用the spec:

      每个案例类都隐式地覆盖了类的一些方法定义 scala.AnyRef 除非已经给出了相同方法的定义 在案例类本身或同一方法的具体定义中 在不同于 AnyRef 的案例类的某些基类中给出。 特别是:

      • 方法equals: (Any)Boolean 是结构相等,其中两个 如果它们都属于所讨论的案例类,则实例是相等的 并且它们具有相等(相对于等于)构造函数参数 (仅限于类的元素,即第一个参数 部分)。

      • 方法hashCode: Int 计算哈希码。如果 数据结构成员映射的 hashCode 方法相等(相对于 to equals) values to equal hash-codes, then the case class hashCode 方法也可以。

      由于构造函数的参数列表是空的,每一个Dimensionequals任何其他Dimension,因此它们的hashCode也必须相同。

      只是不要将案例类与那些奇怪的未初始化vars 混在一起,或者至少要更加小心。

      【讨论】:

        【解决方案3】:

        正如所指出的,hashCode 使用主构造函数 args 来生成其结果。不过,您仍然可以使用 vars 获得案例类的工作版本,例如:

        case class Dimension(var dimensionId: String = "", var name: String = "")
        

        【讨论】:

          猜你喜欢
          • 2011-08-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-19
          相关资源
          最近更新 更多