Scala中List.contains(x)返回false,但exists(_.== x)返回true。

5

我正在使用Scala处理一些简单的数据结构和集合,但我注意到了一些奇怪的行为。以下是对象:

class State (protected val trackmap: Map[Int, List[String]]) {

  override def clone : State = {
    new State(Map() ++ trackmap)
  }

  override def toString = { "State: " + trackmap.toString }

  def equals (other: State) : Boolean = {
    //println("Comparing " + trackmap + " to " + other.trackmap)
    trackmap == other.trackmap  

  }

  def == (other: State) : Boolean = {
    this equals other
  }
}

我的相关测试:

  test("state equality") {
    val state = new State( Map(1 -> List("engine"), 2 -> List("a"), 3 -> List("b")) )

    expect(true) { state equals state.clone }
    expect(true) { state == state.clone }
    expect(false) { state == new State(Map(1 -> List("a"))) }
    expect(false) { state equals new State(Map(1 -> List("a"))) }

    expect(true) { List(state).exists( _.equals (state.clone) )}
    expect(true) { List(state).exists( _.== (state.clone) )}
    expect(true) { List(state).contains( state.clone )}
  }

除了最后一个以外,所有都通过了,但我希望最后一个也能通过。虽然我还没有查看Scala源代码,但我认为contains基本上是实现为第二个exists调用的。
3个回答

12

您没有重写 Scala 中的 实际 equals 方法,这就是为什么它的行为会很奇怪。

请按照以下方式重新编写您的 equals 方法,然后问题应该就可以解决:

override def equals (other: Any) : Boolean = {
    other match{
      case that: State =>
        //println("Comparing " + trackmap + " to " + other.trackmap)
        trackmap == that.trackmap
      case _ => false
    }
}

注意,在Scala中,equals方法接受类型为Any的参数,而不是State,您需要为其添加override关键字。

顺便提一下,您甚至不需要使用==方法,因为Scala会自动重映射它到equals方法!


4
请注意,这也可以通过以下事实得到证明:虽然 List(state).exists( _ == state.clone ) 返回 true,但 List(state).exists( _ == (state.clone:Any) ) 返回 false - Régis Jean-Gilles
2
在您的equals实现中使用模式匹配比使用isInstanceOf/asInstanceOf组合更符合惯用语。 - Régis Jean-Gilles
是的没错。我现在就去修复它。谢谢你的建议。(我对Scala还是有些Javaish的) - Amanj Sherwany

4
你的equals方法没有被contains调用,因为你没有在Any上重写默认实现。这一点的线索是编译器没有抱怨缺少override修饰符。
正确的方法签名应该是:
override def equals(other: Any): Boolean

当你调用List(state).exists(_.equals(state.clone))时,编译器会根据参数的类型State选择方法的重载版本。因此它将解析为您的实现。 contains的签名始终需要一个类型为Any的参数,而不管List的类型参数是什么,因此该方法调用将解析为equals的默认实现。请注意,html标记已保留。

3

您的equals==实现不够正确。因此,Scala有了case类。

您的类将如下所示:

case class State(protected val trackmap: Map[Int, List[String]]) {

  override def clone: State = {
    new State(Map() ++ trackmap)
  }

  override def toString = { "State: " + trackmap.toString }
}

如果您希望手动实现它们,您需要实现"Equals"特征,并覆盖以下方法:
override def canEqual(other: Any) = 
  other.isInstanceOf[State]

override def equals(other: Any) = {
  other match {
    case that: State => (that canEqual this) && trackmap == that.trackmap
    case _ => false
  }
}

override def hashCode() = {
  val prime = 41
  prime + trackmap.hashCode
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接