2010-02-13 69 views
16

能否請您分享您對什麼是轉換的Java /斯卡拉(深)集合的互操作性

java.util.HashMap[ 
    java.lang.String, java.util.ArrayList[ 
     java.util.ArrayList[java.lang.Double] 
    ] 
] 
(all of the objects are from java.util or java.lang) 

Map[ 
    String, Array[ 
     Array[Double] 
    ] 
] 
(all of the objects are from scala) 

謝謝, 最優雅的和/或有效的方式的意見 - A

+0

這不是更多的設計問題嗎?這種結構的語義是什麼?你爲什麼要轉換對象? – 2010-02-13 13:38:49

+0

其實我正在通過傑克遜json庫從json文件讀取這些數據(我試過sjson和lift-json都失敗了)。傑克遜json沒有scala API,所以我使用java API來完成這項工作。 – 2010-02-13 22:08:12

回答

7

這樣做的方法已從2.7更改爲2.8。 Retronym的方法適用於2.8。對於2.7,你會改用collections.jcl像這樣:

object Example { 
    import scala.collection.jcl 

    // Build the example data structure 
    val row1 = new java.util.ArrayList[Double]() 
    val row2 = new java.util.ArrayList[Double]() 
    val mat = new java.util.ArrayList[java.util.ArrayList[Double]]() 
    row1.add(1.0) ; row1.add(2.0) ; row2.add(3.0) ; row2.add(4.0) 
    mat.add(row1) ; mat.add(row2) 
    val named = new java.util.HashMap[String,java.util.ArrayList[java.util.ArrayList[Double]]] 
    named.put("matrix",mat) 

    // This actually does the conversion 
    def asScala(thing: java.util.HashMap[String,java.util.ArrayList[java.util.ArrayList[Double]]]) = { 
    Map() ++ (new jcl.HashMap(thing)).map(kv => { 
     (kv._1 , 
     (new jcl.ArrayList(kv._2)).map(al => { 
      (new jcl.ArrayList(al)).toArray 
     }).toArray 
    ) 
    }) 
    } 
} 

所以,總的想法是這樣的:從外面,包裹Java集合在Scala的等價物,然後用地圖在未來的包裹一切水平。如果您想在Scala表示之間進行轉換,請在出口處執行此操作(這裏是末尾的.toArray)。

在這裏你可以看到例如工作:

scala> Example.named 
res0: java.util.HashMap[String,java.util.ArrayList[java.util.ArrayList[Double]]] = {matrix=[[1.0, 2.0], [3.0, 4.0]]} 

scala> val sc = Example.asScala(Example.named) 
sc: scala.collection.immutable.Map[String,Array[Array[Double]]] = Map(matrix -> Array([[email protected], [[email protected])) 

scala> sc("matrix")(0) 
res1: Array[Double] = Array(1.0, 2.0) 

scala> sc("matrix")(1) 
res2: Array[Double] = Array(3.0, 4.0) 
13

我並不是說這一切都是優雅的,但它的工作原理。我明確而非隱含地使用來自JavaConversions的轉換來允許類型推斷稍微有所幫助。在Scala 2.8中新增了JavaConversions

import collection.JavaConversions._ 
import java.util.{ArrayList, HashMap} 
import collection.mutable.Buffer 

val javaMutable = new HashMap[String, ArrayList[ArrayList[Double]]] 

val scalaMutable: collection.Map[String, Buffer[Buffer[Double]]] = 
    asMap(javaMutable).mapValues(asBuffer(_).map(asBuffer(_))) 

val scalaImmutable: Map[String, List[List[Double]]] = 
    Map(asMap(javaMutable).mapValues(asBuffer(_).map(asBuffer(_).toList).toList).toSeq: _*) 

UPDATE:下面是一個使用implicits一組給定的轉換應用到任意嵌套結構的另一種方法。

trait ==>>[A, B] extends (A => B) { 
    def apply(a: A): B 
} 

object ==>> { 
    def convert[A, B](a: A)(implicit a2b: A ==>> B): B = a 

    // the default identity conversion 
    implicit def Identity_==>>[A] = new (A ==>> A) { 
    def apply(a: A) = a 
    } 

    // import whichever conversions you like from here: 
    object Conversions { 
    import java.util.{ArrayList, HashMap} 
    import collection.mutable.Buffer 
    import collection.JavaConversions._ 

    implicit def ArrayListToBuffer[T, U](implicit t2u: T ==>> U) = new (ArrayList[T] ==>> Buffer[U]) { 
     def apply(a: ArrayList[T]) = asBuffer(a).map(t2u) 
    } 

    implicit def HashMapToMap[K, V, VV](implicit v2vv: V ==>> VV) = new (HashMap[K, V] ==>> collection.Map[K, VV]) { 
     def apply(a: java.util.HashMap[K, V]) = asMap(a).mapValues(v2vv) 
    } 
    } 
} 

object test { 
    def main(args: Array[String]) { 

    import java.util.{ArrayList, HashMap} 
    import collection.mutable.Buffer 

    // some java collections with different nesting 
    val javaMutable1 = new HashMap[String, ArrayList[ArrayList[Double]]] 
    val javaMutable2 = new HashMap[String, ArrayList[HashMap[String, ArrayList[ArrayList[Double]]]]] 

    import ==>>.{convert, Conversions} 
    // here comes the elegant part! 
    import Conversions.{HashMapToMap, ArrayListToBuffer} 
    val scala1 = convert(javaMutable1) 
    val scala2 = convert(javaMutable2) 

    // check the types to show that the conversion worked. 
    scala1: collection.Map[String, Buffer[Buffer[Double]]] 
    scala2: collection.Map[String, Buffer[collection.Map[String, Buffer[Buffer[Double]]]]] 
    } 
} 
3

@返璞詞的回答是好,如果你的集合是同質的,但它似乎並沒有爲我工作,當我有一個混合收集。例如:

val x = Map(5 -> Array(1, List(2, 7), 3), 6 -> Map(5 -> List(7, 8, 9)))

這樣的集合轉換成Java,你需要依靠運行時類型,因爲x類型是不是可以在編譯時遞歸地轉換爲Java。這裏是一個可以處理轉換混合階集合到Java的方法:

def convert(x:Any):Any = 
    { 
    import collection.JavaConversions._ 
    import collection.JavaConverters._ 
    x match 
    { 
     case x:List[_] => x.map{convert}.asJava 
     case x:collection.mutable.ConcurrentMap[_, _] => x.mapValues(convert).asJava 
     case x:collection.mutable.Map[_, _] => x.mapValues(convert).asJava 
     case x:collection.immutable.Map[_, _] => x.mapValues(convert).asJava 
     case x:collection.Map[_, _] => x.mapValues(convert).asJava 
     case x:collection.mutable.Set[_] => x.map(convert).asJava 
     case x:collection.mutable.Buffer[_] => x.map(convert).asJava 
     case x:Iterable[_] => x.map(convert).asJava 
     case x:Iterator[_] => x.map(convert).asJava 
     case x:Array[_] => x.map(convert).toArray 
     case _ => x 
    } 
    } 

類似的方法可以用於從Java轉換爲階被寫入。

請注意,此方法的返回類型爲Any,因此要使用返回的值,您可能必須執行強制轉換:val y = convert(x).asInstanceOf[java.util.Map[Int, Any]]