13

Inside a function of mine I construct a result set by filling a new mutable HashMap with data (if there is a better way - I'd appreciate comments). Then I'd like to return the result set as an immutable HashMap. How to derive an immutable from a mutable?

ziggystar
  • 28,410
  • 9
  • 72
  • 124
Ivan
  • 63,011
  • 101
  • 250
  • 382

3 Answers3

13

Discussion about returning immutable.Map vs. immutable.HashMap notwithstanding, what about simply using the toMap method:

scala> val m = collection.mutable.HashMap(1 -> 2, 3 -> 4)
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 4, 1 -> 2)

scala> m.toMap
res22: scala.collection.immutable.Map[Int,Int] = Map(3 -> 4, 1 -> 2)

As of 2.9, this uses the method toMap in TraversableOnce, which is implemented as follows:

def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
    val b = immutable.Map.newBuilder[T, U]
    for (x <- self)
        b += x

    b.result
}
ebruchez
  • 7,760
  • 6
  • 29
  • 41
  • Thanks for introducing 'Map.newBuilder'. I've normally used mutable maps just for the construction of immutable ones. – akauppi Sep 18 '14 at 07:03
  • Is there any tangible difference? I always wondered whether `newBuilder` keeps only one copy of the data in memory or does it perform a full copy of rhe data on `.result`, thus requiring double the memory amount? – matanster Sep 23 '15 at 14:47
  • 1
    You can check the [source](https://github.com/scala/scala/blob/v2.11.7/src/library/scala/collection/mutable/MapBuilder.scala): `MapBuilder.result` just returns `elems`, so it doesn't create a new copy. From this my understanding is that this is exactly the same as doing a `+` operation on the *immutable* collection, and no *mutable* collection is involved. – ebruchez Sep 23 '15 at 16:06
8
scala> val m = collection.mutable.HashMap(1->2,3->4)
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 4, 1 -> 2)

scala> collection.immutable.HashMap() ++ m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4)

or

scala> collection.immutable.HashMap(m.toSeq:_*)
res2: scala.collection.immutable.HashMap[Int,Int] = Map(1 -> 2, 3 -> 4)
dhg
  • 52,383
  • 8
  • 123
  • 144
  • But the result is an `immutable.Map`, not an `immutable.HashMap` then! The function is meant to return `immutable.HashMap`. – Ivan Jan 30 '12 at 00:25
  • You can specify whatever type you want. If you want the type to be `Map`, do `Map() ++ m` – dhg Jan 30 '12 at 00:26
  • I want `immutable.HashMap`, but `immutable.HashMap ++ m` (where `m` is a `mutable.HashMap`) returns `immutable.Map`. – Ivan Jan 30 '12 at 00:28
  • The result is `immutable.HashMap` if you say `immutable.HashMap(m.toSeq:_*)`, as shown in the answer. – dhg Jan 30 '12 at 00:29
  • By the way, what's your reason for needing the type to be `HashMap`? It's generally best to only assume the "interface" type, like `Map` to allow your program to be flexible for different `Map` implementations. – dhg Jan 30 '12 at 00:37
  • 3
    Yes, it's better to consume as generic types as possible, but to provide as specific types as possible, isn't it? – Ivan Jan 30 '12 at 00:40
  • 1
    @Ivan Not really. If you provide a `HashMap`, then you'll never be able to change into something else. Say, for example, you later need a `LinkedHashMap`, but someone is already depending on you returning a `HashMap`. Inside a module, that's ok. On anything public, keep to interfaces. – Daniel C. Sobral Jan 30 '12 at 02:30
  • @daniel-c-sobral, but aren't there any side effects of turning a `HashMap` into a `Map`? Doesn't `HashMap` provide any functionality a bare `Map` doesn't? It is still not clear for me whether or not I have reasons to return a `HashMap`, I am afraid a reason can actually exists so I can regret switching to a `Map` in future. – Ivan Feb 17 '12 at 01:26
2

If you have a map : logMap: Map[String, String] just need to do : logMap.toMap()

Sandeep Das
  • 1,010
  • 9
  • 22