I have an object with stores information about specific instances. For that, i would like to use a Map
, but as the keys are not by-reference (they aren't, right?) but as hashes provided by the getHashCode
method. For better understanding:
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))
}
}
In cases i have instances with the same hash code the "caching" for the random value will return the same value for both instances even those are not same. Which datastructed is used best in this situation?
Clarification/Edit
I know how this works normally, based on the hashCode
and equals
method. But that is exactly what I want to avoid. I updated my example to make that clearer. :)
EDIT: Based on clarifications to the question, you can create your own Map implementation, and override elemEquals().
The original implementation (in HashMap)
protected def elemEquals(key1: A, key2: A): Boolean = (key1 == key2)
Change this to:
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)
}
Note that to use eq, you need to restrict the key to be an AnyRef, or do a match in the elemEquals() method.
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)
-- Original answer
Map works with hashCode() and equals(). Have you implemented equals() correctly in your obejcts? Note that in Scala, ==
gets translated to a call to equals()
. To get the same behaviour of ==
in Java, use the Scala operator 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")
Here, the case class implements equals() to be object equivalence, in this case:
f1.i == f1.i
You need to override equals() in your objects to include object equality, i.e something like:
override def equals(o: Any) = { o.asInstanceOf[AnyRef] eq this }
This should still work with the same hashCode().
You can also use IdentityHashMap
together with scala.collection.JavaConversions
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With