Such a data structure doesn't exist to my knowledge exactly because it's unsafe - there is no way to reliably encode your particular condition.
But, alas, the Scala Collection Library allows one to implement new rich data structures very quickly with a small amount of fresh code. Here's an implementation of your request:
class ParticularHashMap[A, +B] private(buckets: Vector[List[(A, B)]]) extends Map[A, B]{
def this() = this(Vector.fill(ParticularHashMap.BucketsNo)(List.empty))
def get(key: A) = {
val bucket = buckets(bucketIndex(key))
bucket match {
case List((_, v)) => Some(v) //YOUR SPECIAL OPTIMIZATION !
case list => list.find(key == _._1).map(_._2)
}
}
def iterator = buckets.flatten.iterator
def -(key: A) = mkWithUpdatedBucket(key, _ filterNot (_._1 == key))
def +[B1 >: B](kv: (A, B1)) = mkWithUpdatedBucket(kv._1, insert(kv._1, kv._2.asInstanceOf[B], _))
//if you're wondering why it's Bucket[A, Any] and not Bucket[A, B] it's because of the covariance of B
private def mkWithUpdatedBucket(key: A, f: Bucket[A, Any] => Bucket[A, Any]) = {
val index = bucketIndex(key)
val newBucket = f(buckets(index))
(new ParticularHashMap[A, Any](buckets.updated(index, newBucket))).asInstanceOf[ParticularHashMap[A, B]]
}
private def insert(k: A, v: Any, bucket: List[(A, Any)]): Bucket[A, Any] = bucket match {
case List() => List((k, v))
case (k1, v1) :: kvs if k == k1 => (k, v) :: kvs
case (k1, v1) :: kvs => (k1, v1) :: insert(k, v, kvs)
}
private def bucketIndex(key: A) = Math.abs(key.hashCode()) % ParticularHashMap.BucketsNo
}
object ParticularHashMap {
private val BucketsNo = 256
private type Bucket[+A, +B] = List[(A, B)]
def apply[A, B](kvs: (A, B)*) = new ParticularHashMap[A, B] ++ kvs
}
Please note that this is a naive implementation, but you can improve it too suit your needs.
It will work as expected:
val myMap = ParticularHashMap("hello" -> 2, "world" -> 4)
println(myMap.get("hello")) >> Some(2)
println(myMap.get("world")) >> Some(4)
println(myMap.get("world1")) >> None
But, of course, beware that if you don't respect your contract, bad things will happen. Below is an especially crafted example to show the downside:
val evilMap = ParticularHashMap(1 -> 2)
println(evilMap.get(1)) >> Some(2)
println(evilMap.get(257)) >> Some(2) //BUT 257 IS NOT IN THE MAP !!!