Scala immutable map, when to go mutable?

virtualeyes picture virtualeyes · Apr 13, 2012 · Viewed 14.2k times · Source

My present use case is pretty trivial, either mutable or immutable Map will do the trick.

Have a method that takes an immutable Map, which then calls a 3rd party API method that takes an immutable Map as well

def doFoo(foo: String = "default", params: Map[String, Any] = Map()) {

  val newMap = 
    if(someCondition) params + ("foo" -> foo) else params

  api.doSomething(newMap)
}

The Map in question will generally be quite small, at most there might be an embedded List of case class instances, a few thousand entries max. So, again, assume little impact in going immutable in this case (i.e. having essentially 2 instances of the Map via the newMap val copy).

Still, it nags me a bit, copying the map just to get a new map with a few k->v entries tacked onto it.

I could go mutable and params.put("bar", bar), etc. for the entries I want to tack on, and then params.toMap to convert to immutable for the api call, that is an option. but then I have to import and pass around mutable maps, which is a bit of hassle compared to going with Scala's default immutable Map.

So, what are the general guidelines for when it is justified/good practice to use mutable Map over immutable Maps?

Thanks

EDIT so, it appears that an add operation on an immutable map takes near constant time, confirming @dhg's and @Nicolas's assertion that a full copy is not made, which solves the problem for the concrete case presented.

Answer

dhg picture dhg · Apr 13, 2012

Depending on the immutable Map implementation, adding a few entries may not actually copy the entire original Map. This is one of the advantages to the immutable data structure approach: Scala will try to get away with copying as little as possible.

This kind of behavior is easiest to see with a List. If I have a val a = List(1,2,3), then that list is stored in memory. However, if I prepend an additional element like val b = 0 :: a, I do get a new 4-element List back, but Scala did not copy the orignal list a. Instead, we just created one new link, called it b, and gave it a pointer to the existing List a.

You can envision strategies like this for other kinds of collections as well. For example, if I add one element to a Map, the collection could simply wrap the existing map, falling back to it when needed, all while providing an API as if it were a single Map.