2010-02-13 64 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]]