Scala:包含可变且不可变的集合 [英] Scala: Contains in mutable and immutable sets

查看:176
本文介绍了Scala:包含可变且不可变的集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了我无法理解的可变集的奇怪行为:

I've discovered a strange behavior for mutable sets which I cannot understand:

我有一个要添加到集合中的对象.该类的equals方法将被覆盖.当我将两个不同的对象添加到集合中时,它们为equals方法产生相同的输出,对于contains方法,在可变集和不可变集之间会得到不同的行为.

I have a object which I want to add to a set. The equals method for the class is overridden. When I add two different objects to the set, which produces the same output for equals method, I get a different behavior between mutable and immutable sets for the contains method.

这是代码段:

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

在我看来,包含的两个调用都应产生相同的输出(true).

In my opinion both calls of contains should produce the same output (true).

有人可以帮助我了解这里的区别吗?这是scala不可变集合实现中的错误吗?顺便说一下,我使用的是scala 2.8.1.final

Could anybody help me to understand the difference here or is this a bug in the scala immutable set implementation? By the way, I use scala 2.8.1.final

谢谢.

推荐答案

实现equals()时的规则1:同时实现hashCode().请参见在Java中覆盖等号和hashCode

Rule 1 when implementing equals(): Implement hashCode() at the same time. See Overriding equals and hashCode in Java

在第一个示例中,您正在创建一个可变集,该可变集调用hashCode来建立哈希表.

In the first example, you're creating a mutable set, which calls hashCode to set up the hash table.

在第二个示例中,您使用的是带有一个条目的不可变集,因此Scala实际上使用了名为 Set1 的Set的优化版本. Set1.contains()只是直接使用equals()将一个条目与传递的元素进行比较.看起来像:

In the second, you're using an immutable set with one entry, so Scala actually uses an optimised version of Set called Set1. Set1.contains() just compares the one entry with the passed element using equals() directly. This looks like:

/** 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.

No hashCode is called. There is also a Set2, Set3 and Set4.

因此,如果我们将您的代码更改为:

So if we change your code to be:

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")))

在等号中添加一个hashCode和一个println,输出为:

adding a hashCode and a println in the equals, and the output is:

mutable
hashCode=30936685
hashCode=26956691
mutableSet=Set(test) contains=false
immutable
equals=test
immutableSet=Set(test) contains=true

解释了为什么mutable.contains()无法正常工作.它在错误的哈希表条目中查找对象,equals()甚至没有被调用.而且,毫不奇怪,它没有找到它.

which explains why the mutable.contains() isn't working correctly. It is looking up the object in the wrong hash table entry, equals() doesn't even get called. And, unsurprisingly, it doesn't find it.

您可以使用text.hashCode实现hashCode:

You can implement hashCode using text.hashCode:

override def hashCode: Int = text.hashCode

这篇关于Scala:包含可变且不可变的集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆