How to convert between Scala and Java maps

Scala/Java integration problem: You need to share a Scala map with a Java method, or access a Java map in Scala code.

Update: The JavaConversions object in Scala has been deprecated. For a more modern approach, see my tutorial, How to convert a Java Map to a Scala Map using JavaConverters.

Solution

To use a Java map in Scala code, import the mapAsScalaMap method from Scala’s JavaConversions package, and perform the conversion.

Simply trying to use a Java HashMap with something like a Scala for loop won’t work. To demonstrate this, first create a simple Java HashMap:

val jmap = new java.util.HashMap[String, String]()
jmap.put("fname", "Alvin")
jmap.put("lname", "Alexander")

Next, attempt to use the HashMap in a Scala for loop. You’ll see that it won’t work:

scala> for ((k,v) <- jmap) println(s"key: $k, value: $v")
<console>:9: error: value filter is not a member of java.util.HashMap[String,String]
              for ((k,v) <- jmap) println(s"key: $k, value: $v")
                            ^

However, by simply importing the correct method from the JavaConversions package, the for loop magically works:

scala> import scala.collection.JavaConversions.mapAsScalaMap
import scala.collection.JavaConversions._

scala> for ((k,v) <- jmap) printf("key: %s, value: %s\n", k, v)
key: lname, value: Alexander
key: fname, value: Alvin

Discussion

As demonstrated in Recipe 17.1 in the Scala Cookbook, “Going to and from Java Collections,” the JavaConversions package makes the process of going back and forth between Java collections very simple. The key to performing conversions is knowing which collections map to each other. There are lookup tables in that recipe that will help you make that choice.

Some developers don’t like the “magic” of implicit conversions, so if you prefer to be explicit about the conversion, call the conversion method from the JavaConversions package directly (after importing it):

scala> val smap = mapAsScalaMap(jmap)
smap: scala.collection.mutable.Map[String,String] = 
  Map(lname -> Alexander, fname -> Alvin)

Java maps that don’t use generics

You can run into a problem when working with Java maps that don’t use generics. For instance, I ran into a problem when extending a Java interface as a Scala class. The BasicPlayerListener is a Java interface whose opened method is declared like this:

// java
void opened(java.lang.Object stream, java.util.Map properties);

The beginning of my Scala class definition looks like this:

class MyListener extends BasicPlayerListener {
  def opened(stream: Any, properties:Map[_, _] ) { // code here ...

Because I was implementing this interface, I needed to declare the Map in my Scala opened method as follows to work with the java.util.Map reference in the opened method of the Java interface:

def opened(stream: Any, properties:Map[_, _] ) { 
  println("opened() called")
}

Notice that it was necessary to define the map as Map[_, _]. Fortunately Eclipse and the ScalaIDE project provided help when I was working on this override, because I first attempted to define the map as Map[Any, Any]:

Note that java.util.Map does not match java.util.Map[Any,Any]. To implement a raw type, use java.util.Map[_, _].

In addition to defining the Map with that syntax, I also needed these two import statements in my Scala code to make everything work:

import scala.collection.JavaConversions._
import java.util.Map

See Also

  • Recipe 17.1, “Going to and from Java Collections,” for details on the JavaConversions object
  • The JavaConversions object: http://www.scala-lang.org/api/current/index.html#scala.collection.JavaConversions$