如何将嵌套的Scala集合转换为嵌套的Java集合

17
我在Scala和Java之间编译时遇到了问题。
我的Java代码需要一个

java.util.Map<Double, java.lang.Iterable<Foo>>

我的scala代码有一个

Map[Double, Vector[Foo]]
我遇到编译错误:
error: type mismatch;
found   : scala.collection.immutable.Map[scala.Double,Vector[Foo]
required: java.util.Map[java.lang.Double,java.lang.Iterable[Foo]]

看起来scala.collection.JavaConversions不适用于嵌套的集合,即使Vector可以隐式转换为Iterable。除了手动迭代Scala集合并进行转换之外,有没有什么方法可以使类型正常工作?

5个回答

7

scala.collection.JavaConversions 应该被弃用。我认为,最好明确指定转换发生的时间和位置,使用 scala.collection.JavaConverters。在您的情况下:

import scala.collection.JavaConverters._

type Foo = Int // Just to make it compile
val scalaMap = Map(1.0 -> Vector(1, 2)) // As an example

val javaMap = scalaMap.map { 
  case (d, v) => d -> v.toIterable.asJava
}.asJava

谢谢您指引我使用JavaConversions。我之前不知道这个工具。为了让我的方法返回类型正常工作,我还需要进行一些麻烦的Java泛型通配符操作,即将声明的返回类型转换为public <T extends Iterable<Foo>> Map<Double,T> createMap() - Adam K

6

这更适合我的需求:

  def toJava(m: Any): Any = {
    import java.util
    import scala.collection.JavaConverters._
    m match {
      case sm: Map[_, _] => sm.map(kv => (kv._1, toJava(kv._2))).asJava
      case sl: Iterable[_] => new util.ArrayList(sl.map( toJava ).asJava.asInstanceOf[util.Collection[_]])
      case _ => m
    }
  }

4
我写了这个通用函数,适用于我的需要。
def toJava(x: Any): Any = {
  import scala.collection.JavaConverters._
  x match {
    case y: scala.collection.MapLike[_, _, _] => 
      y.map { case (d, v) => toJava(d) -> toJava(v) } asJava
    case y: scala.collection.SetLike[_,_] => 
      y map { item: Any => toJava(item) } asJava
    case y: Iterable[_] => 
      y.map { item: Any => toJava(item) } asJava
    case y: Iterator[_] => 
      toJava(y.toIterable)
    case _ => 
      x
  }
}

不错!我也想知道你是否写了一个类似的递归 toScala 函数。 - vishvAs vAsuki
我最终编写的递归toScala函数如下: def toScala(x: Any): Any = { import collection.JavaConversions._ x match { case y: java.util.Map[, ] => mapAsScalaMap(y).map{ case (d, v) => toScala(d) -> toScala(v) } case y: java.lang.Iterable[] => iterableAsScalaIterable(y).toList.map { item: Any => toScala(item) } case y: java.util.Iterator[] => toScala(y) case _ => x } } - vishvAs vAsuki
我在这里另外发布了一个相关的问题:http://stackoverflow.com/questions/43462034/cannot-resolve-symbol-asjava-intellij-idea - vishvAs vAsuki

0

所有其他解决方案都是从 AnyAny,这对于像 Scala 这样的强类型语言来说相当糟糕。
这里有一个尽可能保持类型的解决方案:

trait AsJava[T, R] {
  def apply(o: T): R
}

object AsJava extends LowPriorityAsJava {
  implicit class RecursiveConverter[T](o: T) {
    def asJavaRecursive[R](implicit asJava: AsJava[T, R]): R = asJava(o)
  }

  implicit lazy val longAsJava: AsJava[Long, lang.Long] = new AsJava[Long, lang.Long] {
    def apply(o: Long): lang.Long = Long.box(o)
  }

  implicit lazy val intAsJava: AsJava[Int, lang.Integer] = new AsJava[Int, lang.Integer] {
    def apply(o: Int): lang.Integer = Int.box(o)
  }

  implicit lazy val doubleAsJava: AsJava[Double, lang.Double] = new AsJava[Double, lang.Double] {
    def apply(o: Double): lang.Double = Double.box(o)
  }

  implicit def mapAsJava[K, V, KR, VR](
      implicit
      keyAsJava: AsJava[K, KR],
      valueAsJava: AsJava[V, VR]
  ): AsJava[Map[K, V], util.Map[KR, VR]] =
    new AsJava[Map[K, V], util.Map[KR, VR]] {
      def apply(map: Map[K, V]): util.Map[KR, VR] =
        map.map { case (k, v) => (keyAsJava(k), valueAsJava(v)) }.asJava
    }

  implicit def seqAsJava[V, VR](implicit valueAsJava: AsJava[V, VR]): AsJava[Seq[V], util.List[VR]] =
    new AsJava[Seq[V], util.List[VR]] {
      def apply(seq: Seq[V]): util.List[VR] = seq.map(valueAsJava(_)).asJava
    }

  implicit def setAsJava[V, VR](implicit valueAsJava: AsJava[V, VR]): AsJava[Set[V], util.Set[VR]] =
    new AsJava[Set[V], util.Set[VR]] {
      def apply(set: Set[V]): util.Set[VR] = set.map(valueAsJava(_)).asJava
    }

  implicit lazy val anyAsJava: AsJava[Any, AnyRef] = new AsJava[Any, AnyRef] {
    def apply(o: Any): AnyRef = o match {
      case x: Map[Any, Any] => mapAsJava(anyAsJava, anyAsJava)(x)
      case x: Seq[Any]      => seqAsJava(anyAsJava)(x)
      case x: Set[Any]      => setAsJava(anyAsJava)(x)
      case x: Long          => longAsJava(x)
      case x: Int           => intAsJava(x)
      case x: Double        => doubleAsJava(x)
      case x                => x.asInstanceOf[AnyRef]
    }
  }
}

trait LowPriorityAsJava {
  implicit def otherAsJava[T]: AsJava[T, T] = new AsJava[T, T] {
    def apply(o: T): T = o
  }
}

使用方法:

Seq(Seq.empty[Int]).asJavaRecursive

0

如果有人在spark-scala中寻找解决方案,请尝试以下方法:

import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema

这里,y是嵌套的WrapperArray。

y match {
          case x : WrappedArray[x] =>
             (x.map(f => f match {case z: GenericRowWithSchema => z.mkString(",").toString()
                                                case z:Any => z  })).asJavaCollection
          case _ => row.get(i).asInstanceOf[Object]
        }

上述代码有两个作用: 1)如果包装数组具有原始数据类型,则条件case_通过。 2)如果包装数组具有复杂数据类型(例如struts),则执行通用行与模式的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接