3

For some weird reason, there is this one method merged that is only declared in scala.collection.immutable.HashMap but not in generic Map trait (perhaps its implementation makes it very unpalatable to other Map implementations?).

So I need to convert my Map[A, B] to HashMap[A, B] and so far I cannot find an easy way to do so. This is my current implementation to hack around def to[Col[_]] which expects unary higher-kind instead of binary higher-kind.

val m1 = Map("foo" -> 1)
val m2 = Map("foo" -> 2, "bar" -> 2)

type HM[_] = HashMap[String, Int]
(m1.to[HM] merged m2.to[HM]) { case ((k1, v1), (k2, v2)) => (k1, v1 + v2) } // Map("foo" -> 3, "bar" -> 2)

It works as expected but I can't help but to think there must be a better way to convert from Map to HashMap (given it's the default implementation).

Or perhaps in more general, better way to access default implementation of scala collection generic traits?

Daniel Shin
  • 5,086
  • 2
  • 30
  • 53
  • Maybe `HashMap(m1.toSeq: _*)`? See http://stackoverflow.com/questions/7076128/best-way-to-merge-two-maps-and-sum-the-values-of-same-key for other options to merge maps. – Łukasz May 02 '16 at 11:36
  • If you use operations that are only available in `HashMap`, why not declare your maps as `HashMap` in the first place? – Rüdiger Klaehn May 02 '16 at 11:39
  • @Łukasz Yeah I've seen that question but this portion of my code is very performance sensitive and using `toSeq` will require 2 iterations. On the same note, `merged` is supposedly very efficient so it'd be nice to use it. – Daniel Shin May 02 '16 at 12:00
  • @RüdigerKlaehn Because I get those map by calling `toMap` on array. I can fold it manually to `HashMap` but rather not to if possible. – Daniel Shin May 02 '16 at 12:02
  • So if you want to be fast you probably want to take your array and do `HashMap.empty[String, Int] ++ array`. To do this inline you will need to help type inference to not get a plain `Map` again. `((HashMap.empty ++ array): HashMap[String, Int]) merged ...` – Łukasz May 02 '16 at 12:14
  • Why not directly convert your arrays to an `HashMap` then (using `to` just like you do here)? Is there a point in creating an intermediate `Map`? – Régis Jean-Gilles May 02 '16 at 13:18

3 Answers3

0

You can start with an empty HashMap and add all elements there:

immutable.HashMap.empty ++= yourMap
Roman
  • 64,384
  • 92
  • 238
  • 332
0

This will not answer your question, but it is a solution to your problem. It will be faster just to:

  1. Implement the merged method that you need for map,
  2. Use the ++ method defined on map to make a merge.
Ayoub
  • 361
  • 4
  • 15
0

The most efficient way to do the conversion, assuming the original map is already a immutable.HashMap, is immutable.HashMap.from(originalMap) because it returns the original map when it is already a immutable.HashMap.

But consider that the immutable.Map constructor builds an immutable.HashMap only if the size is greater than 4.

scala> val generic4 = Map("a" -> 1, "b" -> 2, "c" -> 3,  "d" -> 4)
val generic4: Map[String, Int] = Map(a -> 1, b -> 2, c -> 3, d -> 4)

scala> generic4.getClass
val res5: Class[? <: Map[String, Int]] = class scala.collection.immutable.Map$Map4

scala> val generic5 = Map("a" -> 1, "b" -> 2, "c" -> 3,  "d" -> 4, "e" -> 5)
val generic5: Map[String, Int] = HashMap(e -> 5, a -> 1, b -> 2, c -> 3, d -> 4)

scala> generic5.getClass
val res6: Class[? <: Map[String, Int]] = class scala.collection.immutable.HashMap

scala> val specific5 = HashMap.from(generic5)
val specific5: scala.collection.immutable.HashMap[String, Int] = HashMap(e -> 5, a -> 1, b -> 2, c -> 3, d -> 4)

scala> specific5 eq generic5
val res7: Boolean = true
Readren
  • 994
  • 1
  • 10
  • 18