2010-02-13 26 views
16

BirJava/Scala (derinlik) koleksiyonları birlikte çalışabilirlik

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) 

sayesinde, dönüştürme en zarif ve/veya etkili bir yoldur ne fikirlerinizi paylaşabilir misiniz -

+0

Bu daha fazla bir tasarım problemi değil mi? Bu yapının semantiği nedir? Neden nesneleri dönüştürmek istiyorsun? –

+0

Aslında bu verileri bir json dosyasından jackson json kütüphanesine (sjson ve lift-json denedim hem başarısız oldu) okuyordum. Jackson json'un bir scala api'si yok, bu yüzden işi yapmak için java API'yi kullandım. –

cevap

7

Bunu yapmanın yöntemi 2,7'den 2,8'e değiştirildi. Retronym yöntemi 2.8 için iyi çalışıyor. 2.7 için, bunun yerine şöyle collections.jcl kullanmak:

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

Yani, genel bir fikir şudur: dışarıdan bir Scala eşdeğer Java koleksiyonunu sarın, ardından haritayı kullanmak sonraki her şeyi sarmak için seviyesi. Scala gösterimleri arasında geçiş yapmak isterseniz, çıkış yolunda bunu yapın (burada, .toArray uçlarında).

Ve burada çalışan örnek görebilirsiniz:

sizin koleksiyonları homojen olup olmadığını retronym cevabı iyidir, ama ben karışık koleksiyonu vardı benim için çalışmak değildi @
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

Tamamen bu kadar zarif olduğunu iddia etmiyorum, ama işe yarıyor. Tür çıkarımının biraz yardımcı olmasına izin vermek için JavaConversions arasındaki dönüşümleri dolaylı olarak açıkça kullanmak yerine kullanıyorum. JavaConversions, Scala 2.8'de yeni.

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: _*) 

GÜNCELLEME: İşte bir keyfi iç içe yapıya dönüşüm belirli bir grubu uygulamak için implicits kullanan alternatif bir yaklaşımdır.

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

. Örneğin: x tipi yinelemeli derleme zamanında java dönüştürülebilir bir şey değildir çünkü

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

java için böyle bir koleksiyon dönüştürmek için, çalışma zamanı türleri güvenmek gerekir.

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

benzer bir yöntem skalasına java dönüştürme için yazılabilir: burada java karışık skala koleksiyonları dönüştürme işleyebilir bir yöntemdir.

Bu yöntemin dönüş türünün Any olduğunu unutmayın, böylece döndürülen değeri kullanmak için bir yayınlama gerçekleştirmeniz gerekebilir: val y = convert(x).asInstanceOf[java.util.Map[Int, Any]].