Published 2014-03-10.
Last modified 2019-07-20.
Time to read: 4 minutes.
Scala 2.13 introduced many new converters, most of which are detailed in this and the following lecture.
The code for this lecture is provided in collections.ConverterFun
.
Converter History
Back in the mists of time JavaConversions
was the only way to convert between Scala and Java collections.
JavaConversions
assumed that every collection needs to be wrapped and did not allow for control of what is converted.
The unnecessary conversions added unnecessary overhead, so JavaConversions
was quickly replaced by the scala.collection.JavaConverters
object.
JavaConverters
did a better job, however JavaConversions
was not removed for many releases.
Scala 2.12 deprecated JavaConversions
.
For Scala 2.13, the JavaConverters
object was moved to the scala.jdk
package and renamed to CollectionConverters
.
Prior to Scala 2.13, other converters were provided in the optional scala-java8-compat
library: DurationConverters
, FunctionConverters
, OptionConverters
and StreamConverters
.
These converters were folded into the Scala 2.13 standard library.
All of the scala-java8-compat
methods were placed in the scala.jdk
package; before they had been scattered across a variety of packages (scala.collection
, scala.concurrent
, etc.).
The scala.jdk Package

scala.jdk
package contains the javaapi
package which defines various conversions between Scala and Java types.
Object | Converts Between |
---|---|
CollectionConverters | Scala / Java collections |
DurationConverters | Scala and Java duration types |
FunctionConverters | Scala and Java function types |
FutureConverters | Scala Future and Java CompletionStage
|
OptionConverters | Scala Option and Java Optional types.
|
StreamConverters | Creates Java Stream s that operate on Scala collections (sequentially or in parallel)
|
You bring these into scope with an import
statement, such as.
import scala.jdk.CollectionConverters._ import scala.jdk.DurationConverters._ import scala.jdk.FunctionConverters._ import scala.jdk.FutureConverters._ import scala.jdk.OptionConverters._ import scala.jdk.StreamConverters._
We'll look at almost all of these converters in this lecture. Most programmers just want collection conversions.
import scala.jdk.CollectionConverters._
Beware! Importing CollectionConverters
and FunctionConverters
causes a name collision.
I will show you how to deal with that in the FunctionConverters
section later in this lecture.
Converting to and from Java Collections
scala.jdk.CollectionConverters
provides converters for commonly-used Java collections with asScala
methods and Scala collections with asJava
methods.
Throughout the collection classes (including Option
), methods are named asScala
and asJava
for wrappers, while methods named toJava
and toScala
indicate conversions that copy data to new objects.
Bidirectional Collection Converters
CollectionConverters
provides the following two-way conversions.
If you do a round-trip conversion using these bidirectional converters you end up with the original collection. These bidirectional converters do not make copies of the data, instead they merely wrap it differently.
Remember that a Scala Buffer
is just a mutable List
.
scala.collection.Iterator[T] 🡄🡆 java.util.{ Iterator[T], Enumeration[T] } scala.collection.mutable.Buffer[T] 🡄🡆 java.util.List[T] scala.collection.mutable.Set[T] 🡄🡆 java.util.Set[T] scala.collection.mutable.Map[K,V] 🡄🡆 java.util.{ Map[K,V], Dictionary[K,V] } scala.collection.mutable.ConcurrentMap[T] 🡄🡆 java.util.concurrent.ConcurrentMap[T]
Note that Java's Enumeration
is not used by most software any more, and it has been replaced by Iterator
.
Similarly, Java's Dictionary
has long been deprecated; Map
has been in use in its place for many years.
For example, we can convert from collection.mutable.ListBuffer
to java.util.List
and then back to collection.mutable.ListBuffer
.
scala> scala> import scala.collection._ import collection._
scala> scala> import scala.jdk.CollectionConverters._ import scala.jdk.CollectionConverters._
scala> scala> val list1 = mutable.ListBuffer(1, 2, 3) sl: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
scala> scala> val jl = list1.asJava jl: java.util.List[Int] = [1, 2, 3]
As you know, it is best to define your types, and so this is how you could do that for j1
.
scala> scala> val jl: java.util.List[Int] = list1.asJava // the type of j1 need not be declared, merely added for clarity jl: java.util.List[Int] = [1, 2, 3]
If the program you are working on uses a lot of Java List
s and Scala List
s, you'll find it convenient to define an alias for the Java List
so you can tell them apart without typing out the fully qualified type.
For example this is how to define an alias called JList
for a Java List
.
scala> scala> import java.util.{List => JList} import java.util.{List=>JList}
Now we can declare j1's type in a more convenient manner.
scala> scala> val jl: JList[Int] = list1.asJava // the type of j1 need not be declared, merely added for clarity jl: java.util.List[Int] = [1, 2, 3]
Note that the alias is not used by the Scala REPL when echoing back the results of a computatio.
Now let's convert the Java List
back to a Scala List
.
scala> scala> val s2: mutable.Buffer[Int] = jl.asScala // the type of s2 need not be declared, merely added for clarity s2: scala.collection.mutable.Buffer[Int] = ListBuffer(1, 2, 3)
scala> scala> assert(list1 eq s2)
We discussed assert
and require
in the Predef lecture earlier in this course.
Here is another example.
scala> scala> val i1 = Iterator(1, 2, 3) i1: Iterator[Int] = non-empty iterator
scala> scala> val i2 = i1.asJava.asScala i2: Iterator[Int] = non-empty iterator
scala> scala> assert(i1 eq i2)
And another example.
scala> scala> val m1 = mutable.HashMap(1 -> "eh", 2 -> "bee", 3 -> "sea") m1: scala.collection.mutable.HashMap[Int,String] = Map(2 -> bee, 1 -> eh, 3 -> sea)
scala> scala> val m2 = m1.asJava.asScala m2: scala.collection.mutable.Map[Int,String] = Map(2 -> bee, 1 -> eh, 3 -> sea)
scala> scala> assert(m1 eq m2)
Unidirectional Converters
CollectionConverters
provides the following one-way conversions.
Keep in mind that the default implementation of immutable.Seq
is immutable.List
, so any time you ask for a new Seq
you actually get a new List
.
scala.collection.immutable.Seq[T] 🡆 java.util.List[T] scala.collection.mutable.Seq[T] 🡆 java.util.List[T] scala.collection.immutable.Range 🡆 java.util.List[Int] scala.collection.immutable.Set[T] 🡆 java.util.Set[T] scala.collection.immutable.Map[T] 🡆 java.util.Map[T]
Recall that java.util.List
is an interface, and that Java List
implementations are mutable.
Contrast this with Scala's List
, which is an implementation of the immutable.Seq
trait.
This means that Scala's mutable.Buffer
trait, which is a mutable List
, is the best approximation of Java's List
.
For convenience, let's define JList
as an alias for java.util.List
; that way we'll know that List
refers to Scala's List
.
scala> scala> import java.util.{List => JList} import java.util.{List=>JList}
scala> scala> val list2: Seq[Int] = List(1, 2, 3) list: Seq[Int] = List(1, 2, 3)
scala> scala> val j2: JList[Int] = list2.asJava // the type of j2 need not be declared, merely added for clarity j2: java.util.List[Int] = [1, 2, 3]
scala> scala> val s2: mutable.Buffer[Int] = list2.asJava.asScala // the type of s2 need not be declared, merely added for clarity s2: scala.collection.mutable.Buffer[Int] = Buffer(1, 2, 3)
scala> scala> assert(list2 ne s2)
Scala's mutable and immutable Seq
s are actually implemented as mutable.Buffer
and immutable.List
.
Converting either of these to Java with the asJava
method yields the same type, java.util.List
.
scala> scala> immutable.Seq(1,2,3).asJava res4: java.util.List[Int] = [1, 2, 3]
scala> scala> mutable.Seq(1,2,3).asJava res5: java.util.List[Int] = [1, 2, 3]
Attempting round-trip them from Scala, to Java and back to Scala does not restore the original collection; instead, they are both round-tripped to mutable.Buffer
.
scala> scala> mutable.Seq(1,2,3).asJava.asScala res2: scala.collection.mutable.Buffer[Int] = Buffer(1, 2, 3)
scala> scala> immutable.Seq(1,2,3).asJava.asScala res3: scala.collection.mutable.Buffer[Int] = Buffer(1, 2, 3)
We can also convert Scala and Java Set
s.
Remember that Scala's Set
s are traits (mutable.Set
and immutable.Set
), and Java's Set
is an interface.
The Scala Set
traits and the Java Set
interface each have multiple implementations.
For convenience, let's define JSet
as an alias for java.util.Set
.
scala> scala> import java.util.{ Set => JSet } import java.util.{Set=>JSet}
scala> scala> val set = immutable.HashSet(1, 2, 3) set: scala.collection.immutable.HashSet[Int] = Set(1, 2, 3)
scala> scala> val jSet: JSet[Int] = set.asJava // the type of jSet need not be declared, merely added for clarity jSet: java.util.Set[Int] = [1, 2, 3]
scala> scala> val scala> sSet: mutable.Set[Int] = jSet.asScala // the type of sSet need not be declared, merely added for clarity sSet: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> scala> assert(set ne sSet)
We can also convert Scala and Java Map
s.
Remember that Scala's Map
s are traits (mutable.Map
and immutable.Map
), and Java's Map
is an interface.
The Scala Map
traits and the Java Map
interface each have multiple implementations.
scala> scala> import java.util.{ Map => JMap } import java.util.{Map=>JMap}
scala> scala> val map = immutable.HashMap(2 -> "bee", 1 -> "eh", 3 -> "sea") map: scala.collection.immutable.HashMap[Int,String] = Map(1 -> eh, 2 -> bee, 3 -> sea)
scala>scala> val jMap: JMap[Int, String] = map.asJava // the type of jMap need not be declared, merely added for clarity jMap: java.util.Map[Int,String] = {1=eh, 2=bee, 3=sea}
scala> scala> val sMap: mutable.Map[Int, String] = jMap.asScala // the type of sMap need not be declared, merely added for clarity sMap: scala.collection.mutable.Map[Int,String] = Map(1 -> eh, 2 -> bee, 3 -> sea)
scala> scala> assert(map ne sMap)
We can do the same with sorted maps.
scala> scala> val treeMap = immutable.TreeMap(2 -> "bee", 1 -> "eh", 3 -> "sea") treeMap: scala.collection.immutable.TreeMap[Int,String] = Map(1 -> eh, 2 -> bee, 3 -> sea)
scala> scala> val jMap2: JMap[Int, String] = treeMap.asJava jMap2: java.util.Map[Int,String] = {1=eh, 2=bee, 3=sea}
scala> scala> val sMap2: mutable.Map[Int, String] = jMap2.asScala sMap2: scala.collection.mutable.Map[Int,String] = Map(1 -> eh, 2 -> bee, 3 -> sea)
scala> scala> assert(treeMap ne sMap2)
Exercise
System.getenv
returns a java.util.Map[String, String]
.
Write a Scala console program that accepts a String
to do a case-insensitive search of the values contained in the Map
.
The method should convert the Java Map
to a scala.collection.mutable.Map[String,String]
and then use a filter
to obtain a collection of all the values that contain the given word.
Convert the collection to a List
and display it.
Because IDEA does not pass environment variables to worksheets, and does not provide any mechanism to define environment variables the way it does for run/debug configurations, do not attempt to use a Scala worksheet for this exercise. Also, please use Scala 2.13 syntax; however as of July 19, 2019 there was no released version of Ammonite that embeds that version of Scala.
Hint: you can use the placeholder syntax in a higher-order function in order to examine the values of a Map
: _._2
.
We discussed placeholder syntax in the More Fun With Functions lecture of the Introduction to Scala course.
Although sys.env
returns a Scala Map
, and would normally be recommended for production code, this exercise is intended to give you practice on converting a Java collection to a Scala collection, so use of sys.env
is considered cheating for this exercise.
Solution
package solutions
import scala.jdk.CollectionConverters._
object EnvSearch extends App { def findEnvValuesWith(name: String): List[String] = System.getenv .asScala .filter( _._2.toLowerCase contains name.toLowerCase ) .values .toList
if (args.isEmpty) println("Please provide a text string to search environment values for.") else println(findEnvValuesWith(args.head).mkString("\n")) }
This code is provided in solutions/EnvSearch.scala
.
Run it by typing.
$ scala> sbt "runMain solutions.EnvSearch wordToSearchFor"
OptionConverters
I believe that OptionConverters
should have been included in CollectionConverters
, but the Scala developers disagree with me.
OptionConverters
provides the following two unidirectional conversions.
You can round-trip between scala.Option
and java.util.Optional
, and the results of the round trip will be values and types which are the same as those you started with, however each time one of these conversions is invoked, a new class instance is created and the value is copied into the result.
scala.Option[T] 🡄 java.util.Optional[T] scala.Option[T] 🡆 java.util.Optional[T]
You need to add this import to any code that needs to convert between Scala's Option
and Java's Optional
.
scala> scala> import scala.jdk.OptionConverters._ import scala.jdk.OptionConverters._
We need a Java Optional
instance to play with.
scala> scala> import java.util.Optional import java.util.Optional
scala> scala> val x: Optional[String] = Optional.of("hi") x: java.util.Optional[String] = Optional[hi]
Now let's try out the converters.
scala> scala> x.asScala ^ warning: method asScala in class RichOptional is deprecated (since 2.13.0): Use `toScala` instead res0: Option[String] = Some(hi)
As I mentioned earlier, methods are named asScala
and asJava
for wrappers, while methods named toJava
and toScala
indicate conversions that copy data to new objects.
Since we must use toScala, we know that a new class instance is created for each conversion between Scala Option
and Java Optional
.
scala> scala> val scalaOption = x.toScala scalaOption: Option[String] = Some(hi)
scala> scala> scalaOption.toJava res2: java.util.Optional[String] = Optional[hi]
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.