How to go to and from Java collections in Scala

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 17.1, “How to go to and from Java collections in Scala.”

Problem

You’re using Java classes in a Scala application, and those classes either return Java collections, or require Java collections in their method calls.

Solution

Use the methods of Scala’s JavaConversions object to make the conversions work.

For instance, the java.util.ArrayList class is commonly used in Java applications, and you can simulate receiving an ArrayList from a method in the REPL, like this:

scala> def nums = {
     |     var list = new java.util.ArrayList[Int]()
     |     list.add(1)
     |     list.add(2)
     |     list
     | }
nums: java.util.ArrayList[Int]

Even though this method is written in Scala, when it’s called, it acts just as though it was returning an ArrayList from a Java method:

scala> val list = nums
list: java.util.ArrayList[Int] = [1, 2]

However, because it’s a Java collection, I can’t call the foreach method on it that I’ve come to know and love in Scala, because it isn’t there:

scala> list.foreach(println)
<console>:10: error:

value foreach is not a member of java.util.ArrayList[Int]
              list.foreach(println)
                   ^

But by importing the methods from the JavaConversions object, the ArrayList magically acquires a foreach method:

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

scala> list.foreach(println)
1
2

This “magic” comes from the power of Scala’s implicit conversions, which are demonstrated in Recipe 1.10, “Add Your Own Methods to the String Class”.

Discussion

When you get a reference to a Java collections object, you can either use that object as a Java collection (such as using its Iterator), or you can convert that collection to a Scala collection. Once you become comfortable with Scala collection methods like foreach, map, etc., you’ll definitely want to treat it as a Scala collection, and the way to do that is to use the methods of the JavaConversions object.

As a more thorough example of how the JavaConversions methods work, assume you have a Java class named JavaExamples with the following getNumbers method:

// java
public static List<Integer> getNumbers() {
    List<Integer> numbers = new ArrayList<Integer>();
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);
    return numbers;
}

You can attempt to call that method from Scala code, as shown in this example:

val numbers = JavaExamples.getNumbers()
numbers.foreach(println)  // this won't work

But this code will fail with the following compiler error:

value 'foreach' is not a member of java.util.List[Integer]

To solve this problem, you need to import the JavaConversions.asScalaBuffer method. When you do this, you can either explicitly call the asScalaBuffer method, or let it be used implicitly. The explicit call looks like this:

import scala.collection.JavaConversions.asScalaBuffer

val numbers = asScalaBuffer(JavaExamples.getNumbers)
numbers.foreach(println)

// prints 'scala.collection.convert.Wrappers$JListWrapper'
println(numbers.getClass)

The implicit use looks like this:

import scala.collection.JavaConversions.asScalaBuffer

val numbers = JavaExamples.getNumbers
numbers.foreach(println)

// prints 'java.util.ArrayList'
println(numbers.getClass)

The println(numbers.getClass) calls show that there’s a slight difference in the result between the explicit and implicit uses. Using the explicit asScalaBuffer method call makes the numbers object an instance of collection.convert.Wrappers$JListWrapper, whereas the implicit use shows that numbers is an ArrayList. As a practical matter, you can use either approach, depending on your preferences about working with implicit conversions; they both let you call foreach, map, and other Scala sequence methods.

You can repeat the same example using a Java Map and HashMap. First, create this method in a JavaExamples class:

// java
public static Map<String, String> getPeeps() {
    Map<String, String> peeps = new HashMap<String, String>();
    peeps.put("captain", "Kirk");
    peeps.put("doctor", "McCoy");
    return peeps;
}

Then, before calling this method from your Scala code, import the appropriate JavaConversions method:

import scala.collection.JavaConversions.mapAsScalaMap

You can then call the mapAsScalaMap method explicitly, or allow it to be called implicitly:

// explicit call
val peeps1 = mapAsScalaMap(JavaExamples.getPeeps)

// implicit conversion
val peeps2 = JavaExamples.getPeeps

Again there is a difference between the types of the map objects. In this case, peeps1, which used the explicit method call, has a type of collection.convert.Wrappers$JMapWrapper, whereas peeps2 has a type of java.util.HashMap.

Note that the JavaConversions class has been through a number of changes, and although you’ll see a large number of conversion methods in your IDE, many of them are deprecated. See the latest Scaladoc for the JavaConversions object for up-to-date information.

Conversion tables

One interesting thing that happens during the process of converting Java collections is that you learn more about the Scala collections. For instance, given their names, you might expect a Scala List and a Java List to convert back and forth between each other, but that isn’t the case. A Java List is much more like a Scala Seq or a mutable Buffer.

This is shown in Table 17-1, which shows the two-way conversions that the JavaConversions object allows between Java and Scala collections. This table is adapted from the JavaConversions documentation.

Table 17-1. The two-way conversions provided by the JavaConversions object

Scala collection Java collection
collection.Iterable java.lang.Iterable
collection.Iterable java.util.Collection
collection.Iterator java.util.{Iterator, Enumeration}
collection.mutable.Buffer java.util.List
collection.mutable.Set java.util.Set
collection.mutable.Map java.util.{Map, Dictionary}
collection.mutable.ConcurrentMap java.util.concurrent.ConcurrentMap

As an example of the two-way conversions shown in Table 17-1, the JavaConversions object provides methods that convert between a Java List and a Scala Buffer. The asScalaBuffer method converts a Java List to a Scala Buffer, and bufferAsJavaList converts in the opposite direction, from a Buffer to a List.

Going from Scala collections to Java collections

So far you’ve looked primarily at converting Java collections to Scala collections. You may also need to go in the other direction, from a Scala collection to a Java collection.

If you’re converting a Scala collection to a Java collection, in addition to the two-way conversions shown in Table 17-1, the one-way conversions shown in Table 17-2 are available. Again, these have been adapted from the JavaConversions Scaladoc.

Table 17-2. The Scala to Java one-way conversions provided by the JavaConversions class

Scala collection Java collection
collection.Seq java.util.List
collection.mutable.Seq java.util.List
collection.Set java.util.Set
collection.Map java.util.Map
collection.mutable.Map[String,String] java.util.Properties

For example, assume you want to call the following sum method declared in a Java class named ConversionExamples, which expects a java.util.List<Integer>:

// java
public static int sum(List<Integer> list) {
    int sum = 0;
    for (int i: list) { sum = sum + i; }
    return sum;
}

Putting the conversion tables to work, the following examples show how to pass a Seq, ArrayBuffer, and ListBuffer to the sum method:

import scala.collection.JavaConversions._
import scala.collection.mutable._

val sum1 = ConversionExamples.sum(seqAsJavaList(Seq(1, 2, 3)))
val sum2 = ConversionExamples.sum(bufferAsJavaList(ArrayBuffer(1,2,3)))
val sum3 = ConversionExamples.sum(bufferAsJavaList(ListBuffer(1,2,3)))

There are many other collection conversion possibilities, and hopefully these examples will get you started on the right path.

The JavaConverters object

The Scala JavaConverters object lets you perform conversions in a manner similar to the examples shown, though they don’t offer implicit conversions. Instead they require you to explicitly call asJava or asScala methods to perform the conversions. Be careful, because the object also contains many deprecated methods.

See Also

The Scala Cookbook

This tutorial is sponsored by the Scala Cookbook, which I wrote for O’Reilly:

You can find the Scala Cookbook at these locations:

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.