作为对象引用键的Map?

4

我有一个对象,用于存储特定实例的信息。为此,我想使用 Map,但是键不是按引用传递的(对吧?),而是由 getHashCode 方法提供的哈希值。为了更好的理解:

import collection.mutable._
import java.util.Random

object Foo {
    var myMap = HashMap[AnyRef, Int]()

    def doSomething(ar: AnyRef): Int = {
        myMap.get(ar) match {
            case Some(x) => x
            case None => {
                myMap += ar -> new Random().nextInt()
                doSomething(ar)
            }
        }
    }
}

object Main {
    def main(args: Array[String]) {
        case class ExampleClass(x: String);
        val o1 = ExampleClass("test1")
        val o2 = ExampleClass("test1")

        println(o2 == o1) // true
        println(o2 eq o1) // false

                    // I want the following two lines to yield different numbers
                    // and i do not have control over the classes, messing with their
                    // equals implementation is not possible.
        println(Foo.doSomething(o1))
        println(Foo.doSomething(o2))
    }
}

在存在哈希码相同的实例的情况下,“缓存”随机值将为两个实例返回相同的值,即使它们不相同。在这种情况下,应该使用哪种数据结构最好? 澄清/编辑 我知道通常是根据hashCodeequals方法来确定的。但这正是我想避免的。我更新了我的示例以使其更清晰。 :)
3个回答

6

编辑:根据问题的澄清,您可以创建自己的Map实现,并重写elemEquals()方法。

原始实现(在HashMap中)

protected def elemEquals(key1: A, key2: A): Boolean = (key1 == key2)

将此更改为:

protected def elemEquals(key1: A, key2: A): Boolean = (key1 eq key2)

class MyHashMap[A <: AnyRef, B] extends scala.collection.mutable.HashMap[A, B] {
  protected override def elemEquals(key1: A, key2: A): Boolean = (key1 eq key2)
}

请注意,在使用eq时,您需要将键限制为AnyRef类型,或在elemEquals()方法中进行匹配。
case class Foo(i: Int)
val f1 = new Foo(1)
val f2 = new Foo(1)
val map = new MyHashMap[Foo, String]()
map += (f1 -> "f1")
map += (f2 -> "f2")
map.get(f1) // Some(f1)
map.get(f2) // Some(f2)

Map(映射)是使用hashCode()和equals()方法的。你是否已经正确地实现了对象的equals()方法?请注意,在Scala中,==被翻译为调用equals()方法。要在Java中获得相同的==行为,请使用Scala运算符eq

case class Foo(i: Int)
val f1 = new Foo(1)
val f2 = new Foo(1)
f1 == f2 // true
f1.equals(f2) // true
f1 eq f2 // false

val map = new MyHashMap (f1 -> "f1", f2 -> "f2")
map.get(f1) // Some("f2")
map.get(f2) // Some("f2")

这里,case类实现了equals()方法来判断对象是否相等,例如:

f1.i == f1.i

您需要在对象中重写equals()方法以包含对象相等性,例如:
您需要在对象中重写 equals() 方法以实现对象相等性的比较,如下所示:
override def equals(o: Any) = { o.asInstanceOf[AnyRef] eq this }

这仍然可以使用相同的hashCode()方法。


请看我对huynhjl答案的评论。 :) - Malax
@Malax 不理解,我是说你需要在你的类中覆盖equals方法并使用eq,而不是equals()/==。 - Matthew Farwell

4

1

嗯,根据评论...你可以使用一个覆盖等号的包装器来实现引用语义。

class EqWrap[T <: AnyRef](val value: T) {
  override def hashCode() = if (value == null) 0 else value.hashCode
  override def equals(a: Any) = a match {
    case ref: EqWrap[_] => ref.value eq value
    case _ => false
  }
}
object EqWrap {
  def apply[T <: AnyRef](t: T) = new EqWrap(t)
}

case class A(i: Int)

val x = A(0)
val y = A(0)

val map = Map[EqWrap[A], Int](EqWrap(x) -> 1)
val xx = map.get(EqWrap(x))
val yy = map.get(EqWrap(y))
//xx: Option[Int] = Some(1)
//yy: Option[Int] = None

原始答案(基于不理解问题 - 我必须保留这个以便评论有意义...)

地图已经具备了这个语义(除非我没有理解你的问题)。

scala> val x = A(0)
x: A = A(0)

scala> val y = A(0)
y: A = A(0)

scala> x == y
res0: Boolean = true // objects are equal

scala> x.hashCode
res1: Int = -2081655426

scala> y.hashCode
res2: Int = -2081655426 // same hash code

scala> x eq y
res3: Boolean = false // not the same object

scala> val map = Map(x -> 1)
map: scala.collection.immutable.Map[A,Int] = Map(A(0) -> 1)

scala> map(y)
res8: Int = 1 // return the mapping based on hash code and equal semantic

是的,那就是我想要避免的。我基本上希望反过来。在你的例子中,map(y)不应该返回任何东西,因为y本身不在Map中。 - Malax

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