我发现了一些我无法理解的可变集的奇怪行为:
我有一个想要添加到集合中的对象。该类的equals方法被覆盖。当我向set添加两个不同的对象时,它们为equals方法产生相同的输出,我在contains方法的可变和不可变集之间得到了不同的行为。
以下是代码段:
class Test(text:String){
override def equals(obj:Any) = obj match {
case t: Test => if (t.text == this.text) true else false
case _ => false
}
override def toString = text
}
val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty
mutableSet += new Test("test")
println(mutableSet)
println(mutableSet.contains(new Test("test")))
val immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty
immutableSet += new Test("test")
println(immutableSet)
println(immutableSet.contains(new Test("test")))
这产生输出:
Set(test)
false
Set(test)
true
在我看来,contains的两个调用应该产生相同的输出(true)。
有人可以帮助我理解这里的区别,还是scala不可变集实现中的错误?顺便说一句,我使用scala 2.8.1.final
感谢。
答案 0 :(得分:23)
实现equals()时的规则1:同时实现hashCode()。见Overriding equals and hashCode in Java
在第一个示例中,您正在创建一个可变集,该集调用hashCode来设置哈希表。
在第二个中,您使用的是一个带有一个条目的不可变集,因此Scala实际上使用了一个名为 Set1 的Set的优化版本。 Set1.contains()只是直接使用equals()将一个条目与传递的元素进行比较。这看起来像:
/** An optimized representation for immutable sets of size 1 */
@SerialVersionUID(1233385750652442003L)
class Set1[A] private[collection] (elem1: A) extends Set[A] with Serializable {
override def size: Int = 1
def contains(elem: A): Boolean =
elem == elem1
def + (elem: A): Set[A] =
if (contains(elem)) this
else new Set2(elem1, elem)
def - (elem: A): Set[A] =
if (elem == elem1) Set.empty
else this
def iterator: Iterator[A] =
Iterator(elem1)
override def foreach[U](f: A => U): Unit = {
f(elem1)
}
}
没有调用hashCode。还有Set2,Set3和Set4。
因此,如果我们将您的代码更改为:
class Test(val text:String){
override def equals(obj:Any) = {
println("equals=" + obj)
obj match {
case t: Test => if (t.text == this.text) true else false
case _ => false
}}
override def hashCode(): Int = {
println("hashCode=" + super.hashCode())
super.hashCode()
}
override def toString = text
}
println("mutable")
val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty
mutableSet += new Test("test")
println("mutableSet=" + mutableSet + " contains=" + mutableSet.contains(new Test("test")))
println("immutable")
var immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty
immutableSet += new Test("test")
println("immutableSet=" + immutableSet + " contains=" + immutableSet.contains(new Test("test")))
在equals中添加hashCode和println,输出为:
mutable
hashCode=30936685
hashCode=26956691
mutableSet=Set(test) contains=false
immutable
equals=test
immutableSet=Set(test) contains=true
解释了mutable.contains()无法正常工作的原因。它正在查找错误的哈希表条目中的对象,甚至没有调用equals()。并且,毫不奇怪,它找不到它。
您可以使用text.hashCode实现hashCode:
override def hashCode: Int = text.hashCode
答案 1 :(得分:7)
您还需要覆盖hashCode
。覆盖hashCode
时覆盖必不可少equals
。
注意还有一些没有编译的东西,所以我编辑了一下:
class Test(val text:String){ // added val
override def equals(obj:Any) = obj match {
case t: Test => if (t.text == this.text) true else false
case _ => false
}
override def toString = text
override def hashCode = text.hashCode
}
val mutableSet:scala.collection.mutable.Set[Test] = scala.collection.mutable.Set.empty
mutableSet += new Test("test")
println(mutableSet)
println(mutableSet.contains(new Test("test")))
val immutableSet:scala.collection.immutable.Set[Test] = scala.collection.immutable.Set.empty
val immutableSet2 = immutableSet + new Test("test") // reassignment to val
println(immutableSet2)
println(immutableSet2.contains(new Test("test")))
我建议阅读http://www.artima.com/pins1ed/object-equality.html以获得有关对象平等的更多见解。这是开眼界。