2010-02-13 38 views
16

Ông có thể chia sẻ ý kiến ​​của bạn trên con đường tao nhã và/hoặc hiệu quả nhất để chuyển đổi mộtJava/Scala (sâu) bộ sưu tập khả năng tương tác

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) 

Cảm ơn, là gì - A

+0

Đây có phải là vấn đề thiết kế không? Ngữ nghĩa của cấu trúc này là gì? Tại sao bạn muốn chuyển đổi các đối tượng? –

+0

Thực ra tôi đang đọc dữ liệu này từ một tệp tin json qua thư viện jackson json (tôi đã thử sjson và lift-json cả hai đều thất bại). Jackson json không có scala api nên tôi đã sử dụng java API để thực hiện công việc. –

Trả lời

7

Phương pháp thực hiện việc này đã thay đổi từ 2,7 thành 2,8. Phương thức của Retronym hoạt động tốt cho 2.8. Đối với 2.7, bạn muốn thay vì sử dụng collections.jcl như vậy:

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

Vì vậy, ý tưởng chung là thế này: từ bên ngoài vào, quấn bộ sưu tập Java trong một Scala tương đương, sau đó sử dụng bản đồ để bọc tất cả mọi thứ trong tiếp theo cấp độ. Nếu bạn muốn chuyển đổi giữa các biểu diễn Scala, hãy làm điều đó trên đường ra (ở đây, .toArray ở cuối).

Và đây bạn có thể xem ví dụ làm việc:

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

Tôi không tuyên bố rằng tất cả đều thanh lịch, nhưng nó hoạt động. Tôi sử dụng các chuyển đổi từ JavaConversions rõ ràng hơn là ngầm để cho phép suy luận loại để giúp một chút. JavaConversions là tính năng mới trong 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: _*) 

CẬP NHẬT: Dưới đây là một cách tiếp cận khác có sử dụng implicits để áp dụng một tập hợp của chuyển đổi đến một cấu trúc tùy tiện lồng nhau.

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

@ câu trả lời retronym là tốt nếu các bộ sưu tập của bạn là đồng nhất, nhưng nó dường như không làm việc cho tôi khi tôi đã có một bộ sưu tập khác nhau. Ví dụ:

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

Để chuyển đổi một bộ sưu tập như vậy để java, bạn cần phải dựa trên các loại runtime vì loại x không phải là một cái gì đó có thể được chuyển đổi sang một cách đệ quy java tại thời gian biên dịch. Dưới đây là một phương pháp có thể xử lý việc chuyển đổi các bộ sưu tập scala hỗn hợp thành 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 
    } 
    } 

Một phương pháp tương tự có thể được viết để chuyển từ java sang scala.

Lưu ý rằng kiểu trả về của phương thức này là Any, do đó, để sử dụng giá trị được trả về, bạn có thể phải thực hiện dàn diễn viên: val y = convert(x).asInstanceOf[java.util.Map[Int, Any]].

Các vấn đề liên quan