【问题标题】:Scala List.contains(x) return false, but exists(_.== x) returns trueScala List.contains(x) 返回 false,但 exists(_.== x) 返回 true
【发布时间】:2013-02-23 08:08:48
【问题描述】:

我正在使用 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 调用中实现。

【问题讨论】:

    标签: list scala collections contains exists


    【解决方案1】:

    你没有覆盖 Scala 的 actual 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 方法!

    【讨论】:

    • 请注意,这也可以通过以下事实来证明:List(state).exists( _ == state.clone ) 返回 trueList(state).exists( _ == (state.clone:Any) ) 返回 false
    • equals 实现中使用模式匹配比isInstanceOf/asInstanceOf 组合更惯用。
    • 是的。我现在会修复它。感谢您的建议(我对 Scala 还是有点 Java)
    【解决方案2】:

    contains 没有调用您的equals 方法,因为您没有覆盖Any 上的默认实现。对此的线索是编译器不会抱怨缺少override 修饰符。

    正确的方法签名应该是

    override def equals(other: Any): Boolean
    

    当您调用List(state).exists( _.equals (state.clone) 时,它解析为您的实现,因为编译器知道参数的类型是State。因此,它选择了专门用于该类型的方法的重载变体。

    contains 的签名 总是 接受Any 类型的参数,而不管List 的类型参数如何,因此方法调用将解析为@987654331 的默认实现@。

    【讨论】:

      【解决方案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
      }
      

      【讨论】:

        猜你喜欢
        • 2016-12-05
        • 1970-01-01
        • 2011-01-18
        • 2014-04-24
        • 2015-08-13
        • 1970-01-01
        • 2012-06-29
        • 2013-02-26
        • 1970-01-01
        相关资源
        最近更新 更多