Scala:包含在可变和不可变集合中

标签 scala set immutability scala-collections mutable

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

我有一个要添加到集合中的对象。类的 equals 方法被覆盖。当我将两个不同的对象添加到集合中时,它为 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

在我看来,包含的两次调用都应该产生相同的输出(真)。

任何人都可以帮助我理解这里的区别还是这是 Scala 不可变集实现中的错误?顺便说一句,我用的是scala 2.8.1.final

谢谢。

最佳答案

实现equals()时的规则1:同时实现hashCode()。见 Overriding equals and hashCode in Java

在第一个示例中,您正在创建一个可变集,它调用 hashCode 来设置哈希表。

在第二种情况下,您使用的是一个包含一个条目的不可变集合,因此 Scala 实际上使用了一个名为 的 Set 的优化版本。 Set1 . 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

关于Scala:包含在可变和不可变集合中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7547741/

相关文章:

java - 在 Java 中使用 addAll 超过 Set 的 ConcurrentModification 异常

functional-programming - 在 Elixir 中编写函数 "once"

java - 如何将可变对象编排为跨对象的不可变对象(immutable对象)

json - 使用 circe 转换 Json

linux - 从安装 Zeppelin 的本地计算机到 Docker Spark 集群的连接

java - Riak Java Client 注释在 Scala 案例类中无法识别?

c# - 为什么在 C# 中有 HashSet 而没有 Set?

r - 如何在 R 中进行集合乘法(笛卡尔积)

algorithm - 为什么 Eric Lippert 的不可变二叉树中没有循环?

scala - scala中的私有(private)字段 setter