How to Work with Scala Collections in Java

Uri Bechar
Wix Engineering
4 min readDec 30, 2019

--

Usually you would work with Java collections in Scala, as Java libraries are used in Scala quite often. The other way around does not occur that often. This short tutorial is for Java developers who want to use Scala libraries and need to work with Scala collections.

The implementations of collections in Java and Scala are different, so we need some kind of an adapter from Java collections to Scala collections and vice versa. Fortunately, Scala provides us with such an adapter, the scala.collection.JavaConverters. We will look at how to work with the major collection types List, Set and Map.

Although Scala has mutable collections the common practice is to use immutable collections so the scope of this tutorial will relate to Scala’s immutable collections only.

List

Let’s have a look at this Scala immutable list in the following Example class:

class Example {
def getStrings() : List[String] = List(“string1”, “string2”)
}

In Java, the following code will not compile due to incompatible types:

Example example = new Example();
List<String> strings = example.getStrings();

Let’s use the JavaConverters adapter to transform the Scala list to a Java list:

Example example = new Example();
List<String> strings = JavaConverters.seqAsJavaList(example.getStrings());
strings.forEach(s -> System.out.println(s));

As Scala immutable List extends Seq , we use the adapter’s seqAsJavaList method to do the transformation.

Now let’s change the Example class to a case class with the list as a member:

case class Example(strings : List[String])

In our Java code we want to instantiate the Example case class:

The following will fail compilation:

List<String> strings = new ArrayList<>();
strings.add(“String1”);
Example example = new Example(strings);

We need to change the list to a Scala type list. We can do this with the adapter like this:

List<String> strings = new ArrayList<>();
strings.add(“String1”);
Example example = new Example(JavaConverters.asScalaBuffer(strings).toList());
JavaConverters.seqAsJavaList(example.strings()).forEach(s -> System.out.println(s));

Set

Let’s have a look at this Scala immutable Set in the following SetExample class:

class SetExample() {
def getStringSet() : Set[String] = Set(“string1”, “string2”)
}

The following Java code will fail on incompatible types:

SetExample setExample = new SetExample();
Set<String> stringSet = setExample.getStringSet();

Let’s use the adapter to transform between the types:

SetExample setExample = new SetExample();
Set<String> stringSet = JavaConverters.setAsJavaSet(setExample.getStringSet());
stringSet.forEach(s -> System.out.println(s) );

We will now turn the SetExample to a case class:

case class SetExample(stringSet : Set[String])

And instance it in our Java code, this will fail, we need to convert it to a Scala Set:

Set<String> stringSet = new HashSet<>();
stringSet.add(“String1”);
SetExample setExample = new SetExample(stringSet);

Here is how:

Set<String> stringSet = new HashSet<>();
stringSet.add(“String1”);
SetExample setExample = new SetExample(JavaConverters.asScalaSet(stringSet).toSet());
JavaConverters.setAsJavaSet(setExample.stringSet()).forEach(s -> System.out.println(s) );

In the above code we instantiate the SetExample class using the adapter to change the type to a Scala Set and then use the adapter to convert it back to a Java Set.

Map

Let’s look at this simple class with a getMap function

class MapExample{
def getMap : Map[String,String] = {
Map(“myKey” -> “myValue”)
}
}

Writing the following code will fail on incompatible types:

MapExample example = new MapExample();
Map<String,String> map = example.getMap();

We’ll use the adapter to convert the map to a java map and print the value of “myKey”

MapExample example = new MapExample();
Map<String,String> map = JavaConverters.mapAsJavaMap(example.getMap());
System.out.println(map.get(“myKey”));

Let’s have a look how to pass a java Map to a Scala function that expects a Scala Map type as its argument.

The following case class expects a Scala map in its argument:

case class MapExample(map : Map[String,String])

This code will fail on incompatible types:

Map<String,String> javaMap = new HashMap<>();
javaMap.put(“myKey”, “MyValue”);
MapExample example = new MapExample(javaMap);

Converting a Java map to a Scala map is not as straightforward as the other collection types. First we’ll need to add the following import to our Java class: import scala.Tuple2, then we will need to iterate the Java map and create a List of Tuples and then convert the list to a Scala Seq and then to a Scala map using the Map’s apply method:

Map<String,String> javaMap = new HashMap<>();
javaMap.put(“myKey”, “MyValue”);
//iterate the java map to fill the tuple list
List<Tuple2<String, String>> tuples = javaMap.entrySet()
.stream()
.map(e -> Tuple2.apply(e.getKey(), e.getValue()))
.collect(Collectors.toList());
//convert the list to a Scala Seq
scala.collection.Seq<Tuple2<String,String>> seq = JavaConverters.asScalaBuffer(tuples).toSeq();
//use the Map apply method
MapExample example = new MapExample ((scala.collection.immutable.Map<String, String>) scala.collection.immutable.Map$.MODULE$.apply(seq));
// we can test this by converting back to java list so we can print
Map<String,String> jMap = JavaConverters.mapAsJavaMap(example.map());
System.out.println(jMap.get(“myKey”));

Summary

In the Java code, when getting a collection returned from a Scala function the first thing to do is to use the JavaConverters adapter to convert it to the same Java collection type and then continue working with it. When we need to pass a Java collection to a Scala function which expects a Scala collection in its arguments, do all the manipulations on the Java collection and just before passing the collection as an argument to the Scala function, use the JavaConverters adapter to convert the Java collection type to the same Scala collection type.

Photo by Frederick Tubiermont on Unsplash

--

--