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.
There are two kinds of Maps, the immutable and the mutable. The difference between mutable and immutable objects is that when an object is immutable, the object itself can't be changed. By default, Scala uses the immutable Map.
The map is an immutable collection, so its methods only support the read-only access to the map. For creating a mutable map with read-write methods, you may use the MutableMap interface.
Maps are classified into two types: mutable and immutable. By default Scala uses immutable Map. In order to use mutable Map, we must import scala.
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
.
Using a mutable object is not bad in itself, it becomes bad in a functional programming environment, where you try to avoid side-effects by keeping functions pure and objects immutable.
However, if you create a mutable object inside a function and modify this object, the function is still pure if you don't release a reference to this object outside the function. It is acceptable to have code like:
def buildVector( x: Double, y: Double, z: Double ): Vector[Double] = { val ary = Array.ofDim[Double]( 3 ) ary( 0 ) = x ary( 1 ) = y ary( 2 ) = z ary.toVector }
Now, I think this approach is useful/recommended in two cases: (1) Performance, if creating and modifying an immutable object is a bottleneck of your whole application; (2) Code readability, because sometimes it's easier to modify a complex object in place (rather than resorting to lenses, zippers, etc.)
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