0
# Java Collection Interoperability
1
2
Seamless bidirectional conversion between Scala and Java collections, enabling smooth interoperation with Java libraries and frameworks.
3
4
## CollectionConverters Object
5
6
```scala { .api }
7
object CollectionConverters extends DecorateAsJava with DecorateAsScala
8
```
9
10
The main entry point for collection conversions, combining Java-to-Scala and Scala-to-Java conversion capabilities.
11
12
## Core Import
13
14
```scala
15
import scala.jdk.CollectionConverters._
16
```
17
18
This import provides implicit extension methods for converting between Scala and Java collection types.
19
20
## Scala to Java Conversions
21
22
### DecorateAsJava Conversions
23
24
The following extension methods are available on Scala collections:
25
26
#### Iterable Conversions
27
28
```scala { .api }
29
// Extension methods added by DecorateAsJava
30
implicit class IterableHasAsJava[A](it: Iterable[A]) {
31
def asJava: java.lang.Iterable[A]
32
}
33
34
implicit class BufferHasAsJava[A](b: mutable.Buffer[A]) {
35
def asJava: java.util.List[A]
36
}
37
38
implicit class MutableSeqHasAsJava[A](seq: mutable.Seq[A]) {
39
def asJava: java.util.List[A]
40
}
41
42
implicit class SeqHasAsJava[A](seq: Seq[A]) {
43
def asJava: java.util.List[A]
44
}
45
46
implicit class MutableSetHasAsJava[A](s: mutable.Set[A]) {
47
def asJava: java.util.Set[A]
48
}
49
50
implicit class SetHasAsJava[A](s: Set[A]) {
51
def asJava: java.util.Set[A]
52
}
53
```
54
55
#### Map Conversions
56
57
```scala { .api }
58
implicit class MutableMapHasAsJava[K, V](m: mutable.Map[K, V]) {
59
def asJava: java.util.Map[K, V]
60
}
61
62
implicit class MapHasAsJava[K, V](m: Map[K, V]) {
63
def asJava: java.util.Map[K, V]
64
}
65
66
implicit class ConcurrentMapHasAsJava[K, V](m: concurrent.Map[K, V]) {
67
def asJava: java.util.concurrent.ConcurrentMap[K, V]
68
}
69
```
70
71
#### Dictionary Conversions
72
73
```scala { .api }
74
implicit class DictionaryHasAsJava[K, V](d: mutable.Map[K, V]) {
75
def asJavaDictionary: java.util.Dictionary[K, V]
76
}
77
78
implicit class PropertiesHasAsScala(p: java.util.Properties) {
79
def asScala: mutable.Map[String, String]
80
}
81
```
82
83
## Java to Scala Conversions
84
85
### DecorateAsScala Conversions
86
87
The following extension methods are available on Java collections:
88
89
#### Iterable Conversions
90
91
```scala { .api }
92
// Extension methods added by DecorateAsScala
93
implicit class IteratorHasAsScala[A](it: java.util.Iterator[A]) {
94
def asScala: Iterator[A]
95
}
96
97
implicit class EnumerationHasAsScala[A](e: java.util.Enumeration[A]) {
98
def asScala: Iterator[A]
99
}
100
101
implicit class IterableHasAsScala[A](it: java.lang.Iterable[A]) {
102
def asScala: Iterable[A]
103
}
104
105
implicit class CollectionHasAsScala[A](c: java.util.Collection[A]) {
106
def asScala: Iterable[A]
107
}
108
109
implicit class ListHasAsScala[A](l: java.util.List[A]) {
110
def asScala: mutable.Buffer[A]
111
}
112
113
implicit class SetHasAsScala[A](s: java.util.Set[A]) {
114
def asScala: mutable.Set[A]
115
}
116
```
117
118
#### Map Conversions
119
120
```scala { .api }
121
implicit class MapHasAsScala[K, V](m: java.util.Map[K, V]) {
122
def asScala: mutable.Map[K, V]
123
}
124
125
implicit class ConcurrentMapHasAsScala[K, V](m: java.util.concurrent.ConcurrentMap[K, V]) {
126
def asScala: concurrent.Map[K, V]
127
}
128
129
implicit class DictionaryHasAsScala[K, V](d: java.util.Dictionary[K, V]) {
130
def asScala: mutable.Map[K, V]
131
}
132
```
133
134
## Usage Examples
135
136
### Basic Conversions
137
138
```scala
139
import scala.jdk.CollectionConverters._
140
import java.util.{ArrayList, HashMap}
141
142
// Scala to Java
143
val scalaList = List(1, 2, 3, 4, 5)
144
val javaList: java.util.List[Int] = scalaList.asJava
145
146
val scalaMap = Map("a" -> 1, "b" -> 2)
147
val javaMap: java.util.Map[String, Int] = scalaMap.asJava
148
149
val scalaSet = Set("x", "y", "z")
150
val javaSet: java.util.Set[String] = scalaSet.asJava
151
152
// Java to Scala
153
val javaArrayList = new ArrayList[String]()
154
javaArrayList.add("hello")
155
javaArrayList.add("world")
156
val scalaBuffer = javaArrayList.asScala
157
158
val javaHashMap = new HashMap[String, Int]()
159
javaHashMap.put("one", 1)
160
javaHashMap.put("two", 2)
161
val scalaMutableMap = javaHashMap.asScala
162
```
163
164
### Working with Java Libraries
165
166
```scala
167
import scala.jdk.CollectionConverters._
168
import java.util.stream.Collectors
169
170
// Processing with Java Streams
171
def processWithJavaStreams(data: List[String]): List[String] = {
172
data.asJava
173
.stream()
174
.filter(_.startsWith("A"))
175
.map(_.toUpperCase)
176
.collect(Collectors.toList())
177
.asScala
178
.toList
179
}
180
181
// Using Java concurrent collections
182
import java.util.concurrent.ConcurrentHashMap
183
184
val concurrentMap = new ConcurrentHashMap[String, Int]()
185
concurrentMap.put("count", 0)
186
187
val scalaView = concurrentMap.asScala
188
scalaView.update("count", scalaView("count") + 1)
189
```
190
191
### Interop with Java Frameworks
192
193
```scala
194
import scala.jdk.CollectionConverters._
195
196
// Example: Working with Spring Framework collections
197
class SpringService {
198
import org.springframework.util.MultiValueMap
199
200
def processMultiValueMap(javaMap: MultiValueMap[String, String]): Map[String, List[String]] = {
201
javaMap.asScala.view.mapValues(_.asScala.toList).toMap
202
}
203
}
204
205
// Example: Working with Jackson JSON processing
206
import com.fasterxml.jackson.databind.ObjectMapper
207
208
def parseJsonToScala(json: String): Map[String, Any] = {
209
val mapper = new ObjectMapper()
210
val javaMap = mapper.readValue(json, classOf[java.util.Map[String, Any]])
211
javaMap.asScala.toMap
212
}
213
```
214
215
### Bidirectional Data Flow
216
217
```scala
218
import scala.jdk.CollectionConverters._
219
220
class DataProcessor {
221
// Accept Java collections, process in Scala, return Java collections
222
def processData(javaInput: java.util.List[String]): java.util.Map[String, java.util.List[String]] = {
223
javaInput.asScala
224
.groupBy(_.charAt(0).toString)
225
.view.mapValues(_.asJava)
226
.toMap
227
.asJava
228
}
229
230
// Accept Scala collections, use Java processing, return Scala collections
231
def processWithJavaTools(scalaInput: List[Int]): List[Int] = {
232
import java.util.Collections
233
234
val javaList = scalaInput.asJava
235
Collections.sort(javaList)
236
Collections.reverse(javaList)
237
javaList.asScala.toList
238
}
239
}
240
```
241
242
### Working with Properties
243
244
```scala
245
import scala.jdk.CollectionConverters._
246
import java.util.Properties
247
248
// Load system properties as Scala map
249
val systemProps: mutable.Map[String, String] = System.getProperties.asScala
250
251
// Create Properties from Scala map
252
val config = Map(
253
"server.port" -> "8080",
254
"database.url" -> "jdbc:h2:mem:test"
255
)
256
257
val properties = new Properties()
258
config.foreach { case (k, v) => properties.setProperty(k, v) }
259
260
// Or using asJavaDictionary
261
val scalaMap = mutable.Map("key1" -> "value1", "key2" -> "value2")
262
val dictionary = scalaMap.asJavaDictionary
263
```
264
265
### Iterator and Enumeration Conversions
266
267
```scala
268
import scala.jdk.CollectionConverters._
269
import java.util.{Vector, StringTokenizer}
270
271
// Java Iterator to Scala Iterator
272
val javaVector = new Vector[String]()
273
javaVector.add("a")
274
javaVector.add("b")
275
javaVector.add("c")
276
277
val scalaIterator = javaVector.iterator().asScala
278
scalaIterator.foreach(println)
279
280
// Java Enumeration to Scala Iterator
281
val tokenizer = new StringTokenizer("hello,world,scala", ",")
282
val tokens = tokenizer.asScala.toList // List("hello", "world", "scala")
283
```
284
285
## Performance Considerations
286
287
### View-Based Conversions
288
289
The conversions provided by `CollectionConverters` create lightweight wrappers rather than copying data:
290
291
```scala
292
val scalaList = List(1, 2, 3, 4, 5)
293
val javaList = scalaList.asJava // No copying - creates a wrapper
294
val backToScala = javaList.asScala // Also no copying
295
296
// Changes through one view affect the other (for mutable collections)
297
val scalaMutableList = mutable.ListBuffer(1, 2, 3)
298
val javaView = scalaMutableList.asJava
299
javaView.add(4)
300
println(scalaMutableList) // ListBuffer(1, 2, 3, 4)
301
```
302
303
### When to Copy
304
305
Sometimes you need to create independent copies:
306
307
```scala
308
import scala.jdk.CollectionConverters._
309
310
val scalaList = List(1, 2, 3)
311
val javaListCopy = new java.util.ArrayList(scalaList.asJava) // Independent copy
312
313
val javaMap = new java.util.HashMap[String, Int]()
314
javaMap.put("a", 1)
315
val scalaMapCopy = javaMap.asScala.toMap // Independent immutable copy
316
```
317
318
## Migration Notes
319
320
- In Scala 2.13+, use `scala.jdk.CollectionConverters` instead of the deprecated `JavaConverters`
321
- The `asJava`/`asScala` methods create lightweight wrappers, not copies
322
- For mutable collections, changes through one view affect the other
323
- For independent copies, explicitly create new collections from the converted views