Jay Taylor's notes

back to listing index

How to populate java.util.HashMap on the fly from Scala code? - Stack Overflow

[web search]
Original source (stackoverflow.com)
Tags: stackoverflow.com
Clipped on: 2012-08-14

I am unit testing java code from ScalaTest and would like to populate a java.util.HashMap within the same statement it gets declared. Is it possible to do this in Scala?

asked Oct 1 '10 at 22:30
Image (Asset 1/6) alt=492310

92% accept rate
add comment

5 Answers

up vote 7 down vote accepted

There are a bunch of different ways to accomplish this, only some of which have appeared in the answers thus far.

Method One: Since java.util.HashMap has the constructor HashMap(Map<? extends K,? extends V> m), you could pass it a valid Java Map. And you can do this trivially with Scala's helpful JavaConversions:

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

> val myMap = Map(1->"Hi",2->"Bye")
: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,Hi), (2,Bye))

> val jmap = new java.util.HashMap[Int,String](myMap)  // Need explicit types
: java.util.HashMap[Int,String] = {1=Hi, 2=Bye}

The downsides here are that you have to already have a Scala map (slightly wasteful if you're just going to create a Java one, perhaps), and that you have to specify the types. But it's compact and painless.

Method Two: Alternatively, you can create a new code block as the declaration statement, so you don't even need to have JavaConversions available:

scala> val jmap2 = {              
|   val x = new java.util.HashMap[Int,String]  
|   for ((k,v) <- List(1->"Howdy",2->"partner")) x.put(k,v)
|   x
| }
: java.util.HashMap[Int,String] = {1=Howdy, 2=partner}

Slightly less compact, but completely general, and as efficient (or inefficient) as you care to make it.

Method Three: Also, you can create an anonymous subclass of HashMap as long as it's okay to have a subclass (i.e. .getClass won't return java.util.HashMap), and use the initializer to set your values:

scala> val jmap3 = new java.util.HashMap[Int,String] { 
|   put(1,"Yo"); put(2,"bro")
| }
: java.util.HashMap[Int,String] = {1=Yo, 2=bro}

> jmap3.getClass.getName
: java.lang.String = $anon$1

> jmap3.getClass.getSuperclass.getName
: java.lang.String = java.util.HashMap

The downside is, of course, that it's a subclass of HashMap rather than HashMap, but it's more compact than the assignment-from-code-block version since you don't need to assign the new map to a val.

Method Four: And finally, of course, you can create a method that does what you want and call it instead:

scala> def newJHM[A,B](kv: Iterable[(A,B)]) = {
|   val jhm = new java.util.HashMap[A,B]  
|   kv.foreach(i => jhm.put(i._1,i._2))  
|   jhm                                  
| }                                      
: [A,B](kv: Iterable[(A, B)])java.util.HashMap[A,B]

> val jmap4 = newJHM(Seq(1->"Bye",2->"Now"))  // Type inference now works
: java.util.HashMap[Int,java.lang.String] = {1=Bye, 2=Now}

This is barely less compact than the others and gets the types correct without you having to specify them, so it can be an appealing choice if you're doing this more than once.

P.S. Just for fun, I've shown a variety of ways of getting some key-value pairs into the map, but they're not specific to a given method (except for #1 which requires a map). Mix and match at your preference.

answered Oct 2 '10 at 5:24
Image (Asset 2/6) alt=53.2k374154
add comment

All the methods and constructors of java.util.HashMap are available to you, of course, but that does not provide a way to initialize a map unless you have another one to supply the initial values. The closest you're probably going to get is:

import java.util.HashMap
val jhm
= new HashMap[String, Int]
«code to add key-value pairs to jhm»
answered Oct 1 '10 at 23:09
Image (Asset 3/6) alt=9,75611533
add comment

Building on Randall's answer, you can use JavaConversions to help a bit.

import collection.JavaConversions.asMap
import java.util.HashMap
val jhm
= new HashMap[Int,String](Map(1->"one", 2->"two"))
answered Oct 2 '10 at 0:21
Image (Asset 4/6) alt=17.7k32363
add comment

You could do the map as an anonymous class, and do the initialization as part of the instance initialization of the object.

import java.util.HashMap
val jhm
= new HashMap[String, Int](){
(key1, value1)
(key2, value2)

This actually works equally well in Java (except for requiring double-braces {{}}), but is much more idiomatic in Scala.

answered Oct 2 '10 at 0:36
Image (Asset 5/6) alt=9,03111740
I don't think there is something like static initialization of an object. I guess you are referring to an instance initializer, aren't you? – Ruediger Keller Oct 3 '10 at 17:52
Correct. Editing – Dave Griffith Oct 3 '10 at 18:09
Technically, Scala doesn't have instance initializers. The code that you show is executing the put methods inside the constructor for the anonymous inner class. – Daniel Spiewak Oct 3 '10 at 18:14
add comment

To make something reusable it would be possible to create a new "Map" subtype just for initialization syntax.

It could work something like this (I'm ignoring generics because I don't use them regularly and I'd probably get something wrong):

HashMap hm=new HashMap(
new InitMap(
new String[]{"one", "two", "three"},
new int[]   {  1  ,   2  ,    3   };

There would be more code involved in the InitMap class but it would be reusable and fairly straight-forward (I really like array initialization syntax for this kind of stuff).

Thinking about it, the InitMap class wouldn't be too hard. You'd probably want to figure out which methods were called and just implement those. Chances are it would only call the KeySet and EntrySet methods.

Of course at this rate you could simple create a helper method that took the two arrays and returned a HashMap or extend HashMap and add a new constructor...

answered Oct 2 '10 at 1:19
Image (Asset 6/6) alt=23.4k23377
add comment

Your Answer

community wiki

Not the answer you're looking for? Browse other questions tagged or ask your own question.