This is an improvement of the first proposed solution by @cheeken. I warmly suggest not to adopt the second, unless you are working on a trivial project. With the second approach you can't enforce that all the items you put in the map have their hash computed with the same hasher and this might lead to wrong and unexpected behaviour which is hard to explain at runtime.
The right way to go is to take ispiration from the HashMap inside Scala library:
@SerialVersionUID(2L)
class HashMap[A, +B] extends Map[A,B] with MapLike[A, B, HashMap[A, B]] with Serializable with CustomParallelizable[(A, B), ParHashMap[A, B]] {
override def size: Int = 0
override def empty = HashMap.empty[A, B]
def iterator: Iterator[(A,B)] = Iterator.empty
override def foreach[U](f: ((A, B)) => U): Unit = { }
def get(key: A): Option[B] =
get0(key, computeHash(key), 0)
override def updated [B1 >: B] (key: A, value: B1): HashMap[A, B1] =
updated0(key, computeHash(key), 0, value, null, null)
override def + [B1 >: B] (kv: (A, B1)): HashMap[A, B1] =
updated0(kv._1, computeHash(kv._1), 0, kv._2, kv, null)
override def + [B1 >: B] (elem1: (A, B1), elem2: (A, B1), elems: (A, B1) *): HashMap[A, B1] =
this + elem1 + elem2 ++ elems
// TODO: optimize (might be able to use mutable updates)
def - (key: A): HashMap[A, B] =
removed0(key, computeHash(key), 0)
protected def elemHashCode(key: A) = key.##
protected final def improve(hcode: Int) = {
var h: Int = hcode + ~(hcode << 9)
h = h ^ (h >>> 14)
h = h + (h << 4)
h ^ (h >>> 10)
}
private[collection] def computeHash(key: A) = improve(elemHashCode(key))
protected type Merger[B1] = ((A, B1), (A, B1)) => (A, B1)
private[collection] def get0(key: A, hash: Int, level: Int): Option[B] = None
private[collection] def updated0[B1 >: B](key: A, hash: Int, level: Int, value: B1, kv: (A, B1), merger: Merger[B1]): HashMap[A, B1] =
new HashMap.HashMap1(key, hash, value, kv)
protected def removed0(key: A, hash: Int, level: Int): HashMap[A, B] = this
protected def writeReplace(): AnyRef = new HashMap.SerializationProxy(this)
def split: Seq[HashMap[A, B]] = Seq(this)
def merge[B1 >: B](that: HashMap[A, B1], merger: Merger[B1] = null): HashMap[A, B1] = merge0(that, 0, merger)
protected def merge0[B1 >: B](that: HashMap[A, B1], level: Int, merger: Merger[B1]): HashMap[A, B1] = that
override def par = ParHashMap.fromTrie(this)
}
If you look, you can just write the following class:
class CustomHashMap[A,+B](val hashCalculator:HashCalculator[A]) extends HashMap[A,B] {
//protected def elemHashCode(key: A) = key.##
override def elemHashCode(key: A) = hashCalculator(key)
}
You have to be sure that you all the public methods behave correctly, including par (you need to implement a parallel hash map that uses your special hasher) and merge, as well as the empty, which should not return
HashMap.empty[A,B]
but CustomHashMap.empty[A,B]