scala case class 带有循环引用的哈希码

scala case class hashcode with circular references

正如 Scala 文档所说,"Case classes are compared by structure and not by reference"。

我试图创建一个带有循环的链表,然后将这个循环列表中的一个节点添加到一个可变集。当使用常规 class 时,它成功了,但是对于 class 我遇到了堆栈溢出。

我很好奇为什么 Scala 在检查结构之前不检查引用。我的想法是,如果引用相同,则结构保证相同,所以这可以是一个捷径,也可以允许循环引用。

代码:

object CaseClassHashcodeExample {
  def main(args: Array[String]): Unit = {
    val head = new ListNode(0)
    head.appendToTail(1)
    head.appendToTail(2)
    head.appendToTail(3)
    head.next.next.next = head.next
    val set = collection.mutable.Set[ListNode]()
    set.add(head)
    assert(set(head))
  }
}

case class ListNode(var data: Int, var next: ListNode = null) {
  def appendToTail(d: Int): Unit = {
    val end = new ListNode(d)
    var cur = this
    while (cur.next != null)
      cur = cur.next
    cur.next = end
  }
}

I'm curious why Scala doesn't first check for reference before checking for structure

好吧,究竟你想让它检查什么?当前生成的代码看起来像

def hashCode() = 31 * data.hashCode() + next.hashCode()

您可以尝试的一件事是

def hashCode() = 31 * data.hashCode() + (if (next eq this) 0 else next.hashCode())

但它实际上并没有帮助:在您的情况下,它不是 next,它与 this 相同,而是 next.next

next.hashCode 不知道它是从 this.hashCode 调用的,因此无法比较它自己的 nextthis

您可以创建一个考虑一组 "seen" 个对象的辅助方法:

def hashCode() = hashCode(Nil)
def hashCode(seen: List[ListNode]) = if (seen.exists(_ eq this)) 0 else 31 * data.hashCode() + next.hashCode(this :: seen)

但这都大大减慢了常见情况,而且很难真正做到正确。

编辑:对于 equals,引用相等性 首先检查以防万一 类。

class NeverEq {
  override def equals(other: Any) = false
}

case class Test(x: NeverEq)

val x = Test(new NeverEq())
println(x == x)

打印 true 如果只比较结构,将是 false

但它实际上对循环引用没有帮助。假设您有一个没有 dataListNode 类型来简化,它实现了这样的相等性:

def equals(other: Any) = (this eq other) || (other match {
  case other: ListNode => this.next == other.next
})

并想检查 node1 == node2 是否 node1.nextnode2,反之亦然:

node1 <--> node2
  1. node1 == node2 减少为 (node1 eq node2) || (node1.next == node2.next).
  2. node1 eq node2 是假的,所以减少到 node1.next == node2.next,即 node2 == node1.
  3. node2 eq node1 为假,因此减少为 node2.next == node1.next,即 node1 == node2...