可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I working on a Scala program that calls a function from a Java library, processes the results, and spits out a CSV.
The Java function in question looks like this:
Map<String, Map<String, AtomicLong>> getData();
The Scala:
import scala.collection.JavaConversions._
def analysisAndCsvStuff(data: Map[String, Map[String, AtomicLong]]): Unit { ... }
The error:
type mismatch;
found:java.util.Map[java...String,java...Map[java...String,java...AtomicLong]]
required: scala...Map[String,scala...Map[String,java...AtomicLong]]
(The path names were ruining the formatting.)
I'm guessing that the JavaConversions can successfully convert the outer java...Map but not the inner java...Map. I saw this question but I am unsure of how to go about writing an "explicit implicit conversion".
回答1:
Edit: the recommended way is to use JavaConverters
and the .asScala
method:
import scala.collection.JavaConverters._
val myScalaMap = myJavaMap.asScala.mapValues(_.asScala)
Note that you will get mutable maps out of this. You can always use .asScala.toMap
if you want immutable ones.
The original answer with JavaConversions
:
The short answer is: call .mapValues
on the outer map to convert the inner map:
import scala.collection.JavaConversions._
val myScalaMap = myJavaMap.mapValues(_.toMap)
.mapValues
forces the conversion or the outer map to a scala Map
and .toMap
forces the conversion of the inner map to a scala (immutable) map. The immutable part isn't strictly necessary, but anyways...
This is very similar to this anwser. Short example:
scala> val a: java.util.Map[String, java.util.Map[String, String]] = new java.util.HashMap[String, java.util.Map[String, String]]
a: java.util.Map[String,java.util.Map[String,String]] = {}
scala> import scala.collection.JavaConversions._
import scala.collection.JavaConversions._
scala> val myScalaMap = a.mapValues(_.toMap)
myScalaMap: scala.collection.Map[String,scala.collection.immutable.Map[String,String]] = Map()
回答2:
I am unsure of how to go about writing an "explicit implicit conversion"
Using JavaConverters
of course, that phrase suggests a custom implicit.
Here is the back and forth:
scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._
scala> import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.AtomicLong
scala> val m = Map("a" -> Map("b" -> new AtomicLong(7L)))
m: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,java.util.concurrent.atomic.AtomicLong]] = Map(a -> Map(b -> 7))
scala> val j = m mapValues (_.asJava) asJava
warning: there were 1 feature warning(s); re-run with -feature for details
j: java.util.Map[String,java.util.Map[String,java.util.concurrent.atomic.AtomicLong]] = {a={b=7}}
scala> implicit class mapmap[A,B,C](val v:
| java.util.Map[A,java.util.Map[B,C]]) extends AnyVal {
| def asMapMap: Map[A,Map[B,C]] =
| v.asScala.toMap.mapValues(_.asScala.toMap)
| }
defined class mapmap
scala> j.asMapMap
res0: Map[String,Map[String,java.util.concurrent.atomic.AtomicLong]] = Map(a -> Map(b -> 7))
回答3:
First of all, there are 2 versions of Maps
and other major collections in scala, mutable and immutable. The default of use is immutable version, and you can access mutable version through scala.collection.mutable
package.
Because of the mutability of java Collections, converting java Collections to scala mutable Collections is pretty much easier than converting to scala immutable Collections. JavaConversions
is unable to convert java Collections to scala immutable Collections.
Furthermore, JavaCollections
cannot convert your inner collections to scala types.
I suggest you to convert your java Map as a scala data structure as follows.
val javaMap:java.util.HashMap[String,java.util.Map[String,Int]] = new java.util.HashMap()
val scalaMap:Map[String,Map[String,Int]] = javaMap.toMap.map { case (k,v) => k -> v.toMap}
The .tomap
method is defined in scala.collection.mutable.Map
which converts mutable Map
to immutable Map
. and JavaConversions
converts java.util.Map
to scala immutable Map
回答4:
alternatively you can use scalaj-collection library i wrote specifically for this purpose
import com.daodecode.scalaj.collection._
analysisAndCsvStuff(getData.deepAsScala)
that's it. It will convert all nested java collections and primitive types to scala versions. You can also convert directly to immutable data structures using deepAsScalaImmutable
(with some copying overhead of course)