2010-02-13 13 views
16

Können Sie Ihre Meinung teilen, was ist die eleganteste und/oder effiziente Art und Weise eineJava/Scala (tief) Sammlungen Interoperabilität

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) 
der Umwandlung

zu

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

Danke, - A

+0

Ist das nicht mehr ein Designproblem? Was ist die Semantik dieser Struktur? Warum möchten Sie die Objekte konvertieren? –

+0

Eigentlich lese ich diese Daten aus einer JSON-Datei durch Jackson JSON-Bibliothek (ich versuchte sjson und Lift-JSON beide fehlgeschlagen). Jackson Json hat keine Scala API, also habe ich Java API benutzt, um den Job zu machen. –

Antwort

7

Die Methode hierfür wurde von 2,7 auf 2,8 geändert. Die Methode von Retronym funktioniert gut für 2.8. Für 2.7, würden Sie stattdessen collections.jcl wie so verwenden:

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 
    ) 
    }) 
    } 
} 

So ist die allgemeine Idee ist diese: von außen nach innen, wickeln Sie die Java-Sammlung in einer Scala gleichwertig, dann Karte verwenden, um alles in den nächsten zu wickeln Niveau. Wenn Sie zwischen Scala-Darstellungen konvertieren möchten, tun Sie dies auf dem Weg nach draußen (hier die .toArray an den Enden).

Und hier können Sie das Beispiel der Arbeit sehen:

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

Ich behaupte nicht, dass es so elegant ist, aber es funktioniert. Ich verwende die Konvertierungen von JavaConversions explizit, anstatt implizit Typinterferenzen ein wenig zu helfen. JavaConversions ist neu in Scala 2.8.

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: Hier ist ein alternativer Ansatz, den implicits verwendet einen bestimmten Satz von Umwandlungen zu einer beliebig verschachtelten Struktur anzuwenden.

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

@ retronym Antwort gut ist, wenn Ihre Sammlungen homogen sind, aber es schien nicht, für mich zu arbeiten, wenn ich eine gemischte Sammlung hatte. Zum Beispiel:

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

zu konvertieren, eine solche Sammlung zu Java, müssen Sie auf Laufzeittypen verlassen, da die Art der x ist nicht etwas, das rekursiv auf Java bei der Kompilierung umgewandelt werden können. Hier ist ein Verfahren, das gemischte scala Sammlungen Java Umwandlung verarbeiten kann:

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 
    } 
    } 

Ein ähnliches Verfahren kann zur Umwandlung von Java nach Scala geschrieben werden.

Beachten Sie, dass der Rückgabetyp dieser Methode Any ist. Um den zurückgegebenen Wert zu verwenden, müssen Sie möglicherweise eine Umwandlung durchführen: val y = convert(x).asInstanceOf[java.util.Map[Int, Any]].