A library that makes some Scala 2.13 APIs available on Scala 2.11 and 2.12, facilitating cross-building Scala 2.13 and 3.0 code on older versions
—
Seamless bidirectional conversion between Scala and Java collections, enabling smooth interoperation with Java libraries and frameworks.
object CollectionConverters extends DecorateAsJava with DecorateAsScalaThe main entry point for collection conversions, combining Java-to-Scala and Scala-to-Java conversion capabilities.
import scala.jdk.CollectionConverters._This import provides implicit extension methods for converting between Scala and Java collection types.
The following extension methods are available on Scala collections:
// Extension methods added by DecorateAsJava
implicit class IterableHasAsJava[A](it: Iterable[A]) {
def asJava: java.lang.Iterable[A]
}
implicit class BufferHasAsJava[A](b: mutable.Buffer[A]) {
def asJava: java.util.List[A]
}
implicit class MutableSeqHasAsJava[A](seq: mutable.Seq[A]) {
def asJava: java.util.List[A]
}
implicit class SeqHasAsJava[A](seq: Seq[A]) {
def asJava: java.util.List[A]
}
implicit class MutableSetHasAsJava[A](s: mutable.Set[A]) {
def asJava: java.util.Set[A]
}
implicit class SetHasAsJava[A](s: Set[A]) {
def asJava: java.util.Set[A]
}implicit class MutableMapHasAsJava[K, V](m: mutable.Map[K, V]) {
def asJava: java.util.Map[K, V]
}
implicit class MapHasAsJava[K, V](m: Map[K, V]) {
def asJava: java.util.Map[K, V]
}
implicit class ConcurrentMapHasAsJava[K, V](m: concurrent.Map[K, V]) {
def asJava: java.util.concurrent.ConcurrentMap[K, V]
}implicit class DictionaryHasAsJava[K, V](d: mutable.Map[K, V]) {
def asJavaDictionary: java.util.Dictionary[K, V]
}
implicit class PropertiesHasAsScala(p: java.util.Properties) {
def asScala: mutable.Map[String, String]
}The following extension methods are available on Java collections:
// Extension methods added by DecorateAsScala
implicit class IteratorHasAsScala[A](it: java.util.Iterator[A]) {
def asScala: Iterator[A]
}
implicit class EnumerationHasAsScala[A](e: java.util.Enumeration[A]) {
def asScala: Iterator[A]
}
implicit class IterableHasAsScala[A](it: java.lang.Iterable[A]) {
def asScala: Iterable[A]
}
implicit class CollectionHasAsScala[A](c: java.util.Collection[A]) {
def asScala: Iterable[A]
}
implicit class ListHasAsScala[A](l: java.util.List[A]) {
def asScala: mutable.Buffer[A]
}
implicit class SetHasAsScala[A](s: java.util.Set[A]) {
def asScala: mutable.Set[A]
}implicit class MapHasAsScala[K, V](m: java.util.Map[K, V]) {
def asScala: mutable.Map[K, V]
}
implicit class ConcurrentMapHasAsScala[K, V](m: java.util.concurrent.ConcurrentMap[K, V]) {
def asScala: concurrent.Map[K, V]
}
implicit class DictionaryHasAsScala[K, V](d: java.util.Dictionary[K, V]) {
def asScala: mutable.Map[K, V]
}import scala.jdk.CollectionConverters._
import java.util.{ArrayList, HashMap}
// Scala to Java
val scalaList = List(1, 2, 3, 4, 5)
val javaList: java.util.List[Int] = scalaList.asJava
val scalaMap = Map("a" -> 1, "b" -> 2)
val javaMap: java.util.Map[String, Int] = scalaMap.asJava
val scalaSet = Set("x", "y", "z")
val javaSet: java.util.Set[String] = scalaSet.asJava
// Java to Scala
val javaArrayList = new ArrayList[String]()
javaArrayList.add("hello")
javaArrayList.add("world")
val scalaBuffer = javaArrayList.asScala
val javaHashMap = new HashMap[String, Int]()
javaHashMap.put("one", 1)
javaHashMap.put("two", 2)
val scalaMutableMap = javaHashMap.asScalaimport scala.jdk.CollectionConverters._
import java.util.stream.Collectors
// Processing with Java Streams
def processWithJavaStreams(data: List[String]): List[String] = {
data.asJava
.stream()
.filter(_.startsWith("A"))
.map(_.toUpperCase)
.collect(Collectors.toList())
.asScala
.toList
}
// Using Java concurrent collections
import java.util.concurrent.ConcurrentHashMap
val concurrentMap = new ConcurrentHashMap[String, Int]()
concurrentMap.put("count", 0)
val scalaView = concurrentMap.asScala
scalaView.update("count", scalaView("count") + 1)import scala.jdk.CollectionConverters._
// Example: Working with Spring Framework collections
class SpringService {
import org.springframework.util.MultiValueMap
def processMultiValueMap(javaMap: MultiValueMap[String, String]): Map[String, List[String]] = {
javaMap.asScala.view.mapValues(_.asScala.toList).toMap
}
}
// Example: Working with Jackson JSON processing
import com.fasterxml.jackson.databind.ObjectMapper
def parseJsonToScala(json: String): Map[String, Any] = {
val mapper = new ObjectMapper()
val javaMap = mapper.readValue(json, classOf[java.util.Map[String, Any]])
javaMap.asScala.toMap
}import scala.jdk.CollectionConverters._
class DataProcessor {
// Accept Java collections, process in Scala, return Java collections
def processData(javaInput: java.util.List[String]): java.util.Map[String, java.util.List[String]] = {
javaInput.asScala
.groupBy(_.charAt(0).toString)
.view.mapValues(_.asJava)
.toMap
.asJava
}
// Accept Scala collections, use Java processing, return Scala collections
def processWithJavaTools(scalaInput: List[Int]): List[Int] = {
import java.util.Collections
val javaList = scalaInput.asJava
Collections.sort(javaList)
Collections.reverse(javaList)
javaList.asScala.toList
}
}import scala.jdk.CollectionConverters._
import java.util.Properties
// Load system properties as Scala map
val systemProps: mutable.Map[String, String] = System.getProperties.asScala
// Create Properties from Scala map
val config = Map(
"server.port" -> "8080",
"database.url" -> "jdbc:h2:mem:test"
)
val properties = new Properties()
config.foreach { case (k, v) => properties.setProperty(k, v) }
// Or using asJavaDictionary
val scalaMap = mutable.Map("key1" -> "value1", "key2" -> "value2")
val dictionary = scalaMap.asJavaDictionaryimport scala.jdk.CollectionConverters._
import java.util.{Vector, StringTokenizer}
// Java Iterator to Scala Iterator
val javaVector = new Vector[String]()
javaVector.add("a")
javaVector.add("b")
javaVector.add("c")
val scalaIterator = javaVector.iterator().asScala
scalaIterator.foreach(println)
// Java Enumeration to Scala Iterator
val tokenizer = new StringTokenizer("hello,world,scala", ",")
val tokens = tokenizer.asScala.toList // List("hello", "world", "scala")The conversions provided by CollectionConverters create lightweight wrappers rather than copying data:
val scalaList = List(1, 2, 3, 4, 5)
val javaList = scalaList.asJava // No copying - creates a wrapper
val backToScala = javaList.asScala // Also no copying
// Changes through one view affect the other (for mutable collections)
val scalaMutableList = mutable.ListBuffer(1, 2, 3)
val javaView = scalaMutableList.asJava
javaView.add(4)
println(scalaMutableList) // ListBuffer(1, 2, 3, 4)Sometimes you need to create independent copies:
import scala.jdk.CollectionConverters._
val scalaList = List(1, 2, 3)
val javaListCopy = new java.util.ArrayList(scalaList.asJava) // Independent copy
val javaMap = new java.util.HashMap[String, Int]()
javaMap.put("a", 1)
val scalaMapCopy = javaMap.asScala.toMap // Independent immutable copyscala.jdk.CollectionConverters instead of the deprecated JavaConvertersasJava/asScala methods create lightweight wrappers, not copiesInstall with Tessl CLI
npx tessl i tessl/maven-org-scala-lang-modules--scala-collection-compat