equals of case class with non-primitives 对于两个逻辑上相等的实例不能正常工作

equals of case class with non-primitives is not working correctly for two logically equal instances

对于两个逻辑上相等的 case class 实例,Equals 无法正常工作。 请在下面找到简化的案例 classes :-

case class Item(name : String)
case class Basket(items : Array[Item], bills : Map[Int, Int])

def createBasketInstance() : Basket = {
  val maggi = Item("milk")
  val coffee = Item("coffee")

  val bills1 = Map(1 -> 20, 2 -> 75)
  Basket( Array(maggi, coffee), bills1 )
}

val basket1 = createBasketInstance()
val basket2 = createBasketInstance()

basket1.equals(basket2) 的预期结果为真,但实际打印为假。

scalafiddle link : https://scalafiddle.io/sf/iJRKnMk/0

请找到附加的屏幕截图,以确认使用 scalatest 进行相等比较。

这与 case classes 的默认 equals 无关,而与 Arrays 的默认 equals 无关(这是您应该使用 Lists 的数百万个原因之一超过 Arrays 顺便说一句)

在 Scala 中,数组只是普通的 Java 数组,Java 中的数组被特殊对待(因为 JVM)

即使是简单的

Array(1, 2, 3) == Array(1, 2, 3)

将 return 错误。

为什么?因为数组继承 equals 方法的 Object.equals 只比较了 数组引用 而不是它们的内容。

我知道这听起来并没有那么糟糕,但我相信它会变得更糟。

您可能认为只需将数组分配给一个值就可以解决问题,对吗?好吧,事实并非如此。以下也会失败...

def createBasketInstance() : Basket = {
  val maggi = Item("milk")
  val coffee = Item("coffee")
  val arr =  Array(maggi, coffee)
  val bills1 = Map(1 -> 20, 2 -> 75)
  Basket( arr, bills1 )
}

因为这个函数的不同调用的调用栈不能共享引用,所以即使底层内容相同,每个函数调用都会创建一个新的引用。

你是怎么解决这个问题的?

  1. java.utils.Arrays 中有一个名为 Arrays.equals 的方法可以进行深度比较。您可以 override 并构造它,以便比较使用 Arrays.equals。您可能还需要覆盖 hashCode

  2. 写一个从 Array 到字面上任何其他集合

    的隐式转换
  3. 首先不要使用数组。

我会说 #3 是解决此问题的方法