Reputation: 300
Having read this quote on HashTrieMaps on docs.scala-lang.org:
For instance, to find a given key in a map, one first takes the hash code of the key. Then, the lowest 5 bits of the hash code are used to select the first subtree, followed by the next 5 bits and so on. The selection stops once all elements stored in a node have hash codes that differ from each other in the bits that are selected up to this level.
I figured that be a great (read: fast!) collection to store my Map[String, Long] in.
In my Play Framework (using Scala) I have this piece of code using Anorm that loads in around 18k of elements. It takes a few seconds to load (no big deal, but any tips?). I'd like to have it 'in memory' for fast look ups for string to long translation.
val data = DB.withConnection { implicit c ⇒
SQL( "SELECT stringType, longType FROM table ORDER BY stringType;" )
.as( get[String]( "stringType" ) ~ get[Long]( "longType " )
map { case ( s ~ l ) ⇒ s -> l }* ).toMap.withDefaultValue( -1L )
}
This code makes data
of type class scala.collection.immutable.Map$WithDefault
. I'd like this to be of type HashTrieMap
(or HashMap
, as I understand the linked quote all Scala HashMaps are of HashTrieMap?). Weirdly enough I found no way on how to convert it to a HashTrieMap. (I'm new to Scala, Play and Anorm.)
// Test for the repl (2.9.1.final). Map[String, Long]:
val data = Map( "Hello" -> 1L, "world" -> 2L ).withDefaultValue ( -1L )
data: scala.collection.immutable.Map[java.lang.String,Long] =
Map(Hello -> 1, world -> 2)
// Google showed me this, but it is still a Map[String, Long].
val hm = scala.collection.immutable.HashMap( data.toArray: _* ).withDefaultValue( -1L )
// This generates an error.
val htm = scala.collection.immutable.HashTrieMap( data.toArray: _* ).withDefaultValue( -1L )
So my question is how to convert the MapWithDefault to HashTrieMap (or HashMap if that shares the implementation of HashTrieMap)?
Any feedback welcome.
Upvotes: 5
Views: 3789
Reputation: 32729
As the documentation that you pointed to explains, immutable maps already are implemented under the hood as HashTrieMap
s. You can easily verify this in the REPL:
scala> println( Map(1->"one", 2->"two", 3->"three", 4->"four", 5->"five").getClass )
class scala.collection.immutable.HashMap$HashTrieMap
So you have nothing special to do, your code already is using HashMap.HashTrieMap
without you even realizing.
More precisely, the default implementation of immutable.Map
is immutable.HashMap
, which is further refined (extended) by immutable.HashMap.HashTrieMap
.
Note though that small immutable maps are not instances of immutable.HashMap.HashTrieMap
, but are implemented as special cases (this is an optimization). There is a certain size threshold where they start being impelmented as immutable.HashMap.HashTrieMap
.
As an example, entering the following in the REPL:
val m0 = HashMap[Int,String]()
val m1 = m0 + (1 -> "one")
val m2 = m1 + (2 -> "two")
val m3 = m2 + (3 -> "three")
println(s"m0: ${m0.getClass.getSimpleName}, m1: ${m1.getClass.getSimpleName}, m2: ${m2.getClass.getSimpleName}, m3: ${m3.getClass.getSimpleName}")
will print this:
m0: EmptyHashMap$, m1: HashMap1, m2: HashTrieMap, m3: HashTrieMap
So here the empty map is an instance of EmptyHashMap$
. Adding an element to that gives a HashMap1
, and adding yet another element finally gives a HashTrieMap
.
Finally, the use of withDefaultValue
does not change anything, as withDefaultValue
will just return an instance Map.WithDefault
wich wraps the initial map (which will still be a HashMap.HashTrieMap
).
Upvotes: 9