如何将java.util.Map转换为Scala Map

14

一个 Java API 返回一个 java.util.Map<java.lang.String,java.lang.Boolean>;我想把它放进一个 Map[String,Boolean] 中。

所以假设我们有:

var scalaMap : Map[String,Boolean] = Map.empty
val javaMap = new JavaClass().map()   // Returns java.util.Map<java.lang.String,java.lang.Boolean>

你不能使用Map.empty ++ javaMap,因为++方法不知道Java maps。我尝试过:

scalaMap = Map.empty ++ new collection.jcl.MapWrapper[String,Boolean] {
    override def underlying = javaMap
}

且:

scalaMap = Map.empty ++ new collection.jcl.MapWrapper[java.lang.String,java.lang.Boolean] {
    override def underlying = javaMap
  }

这两个都无法编译,因为泛型不匹配 - java.lang.String 不同于 scala 的 String。

有没有什么好方法可以解决这个问题,除了手动复制映射表?

编辑:谢谢大家的回答,收益颇丰。但是,我犯了一个错误,把一个比实际问题简单的问题发到这里了。如果您允许的话,我将概括一下问题 - API 实际返回的是

java.util.Map<java.lang.String, java.util.Map<SomeJavaEnum,java.lang.String>>

我需要将这个数据结构转换为Map[String,Map [SomeJavaEnum,String]]。

这可能看起来并不复杂,但它增加了额外的类型擦除层级,而我找到的将其转换为Scala map的唯一方法是深度复制(使用下面您建议的某些技术)。有没有任何提示?我通过为我的确切类型定义隐式转换来解决了我的问题,因此至少丑陋的部分隐藏在自己的特质中,但仍然感觉有点笨拙,要深度复制整个对象。


我非常喜欢从Scala用户组得到的转换答案。只需要检查它是否有效...但现在已经太晚了,所以很快会回复... - George
4个回答

12

至少在Scala 2.9.2中,通过集合转换有一种更简单的方法:引入 "import collection.JavaConversions._" 并使用 "toMap"。

例如:

// show with Java Map:

scala> import java.util.{Map=>JMap}
scala> val jenv: JMap[String,String] = System.getenv()
jenv: java.util.Map[String,String] = {TERM=xterm, ANT_OPTS=-Xmx512m ...}

scala> jenv.keySet()
res1: java.util.Set[String] = [TERM, ANT_OPTS...]

// Now with Scala Map:

scala> import collection.JavaConversions._
scala> val env: Map[String,String] = System.getenv.toMap // <--- TADA <---
env: Map[String,String] = Map(ANT_OPTS -> -Xmx512m, TERM -> xterm ...)

// Just to prove it's got Scala functionality:

scala> env.filterKeys(_.indexOf("TERM")>=0)
res6: scala.collection.immutable.Map[String,String] = Map(TERM -> xterm, 
  TERM_PROGRAM -> iTerm.app, ITERM_PROFILE -> Default)

使用一个String到Boolean的java.util.map可以正常工作。


6

Scala中的String是一个java.lang.String,但是Scala中的Boolean不是java.lang.Boolean。因此,以下代码可以正常工作:

import collection.jcl.Conversions._
import collection.mutable.{Map => MMap}
import java.util.Collections._
import java.util.{Map => JMap}

val jm: JMap[String, java.lang.Boolean] = singletonMap("HELLO", java.lang.Boolean.TRUE)

val sm: MMap[String, java.lang.Boolean] = jm //COMPILES FINE

但是你仍然面临着Boolean差异的问题。你需要将Java map“折叠”到Scala map中:尝试使用Scala Boolean类型:

val sm: MMap[String, Boolean] = collection.mutable.Map.empty + ("WORLD" -> false)
val mm = (sm /: jm) { (s, t2) => s + (t2._1 -> t2._2.booleanValue) }

然后mm是一个Scala Map,其中包含原始Scala Map的内容以及Java Map中的内容。


实际上,你的答案转换为可变映射,而作者要求不可变映射。 - Dzmitry Lazerka

2

useJavaMap.scala

import test._
import java.lang.Boolean
import java.util.{Map => JavaMap}
import collection.jcl.MapWrapper

object useJavaMap {
  def main(args: Array[String]) {
    var scalaMap : Map[String, Boolean] = Map.empty
    scalaMap = toMap(test.testing())
    println(scalaMap)
  }

  def toMap[K, E](m: JavaMap[K, E]): Map[K, E] = {
    Map.empty ++ new MapWrapper[K, E]() {
      def underlying = m
    }
  }
}

test/test.java

package test;

import java.util.*;

public class test {
    public static Map<String, Boolean> testing() {
        Map<String, Boolean> x = new HashMap<String, Boolean>();
        x.put("Test",Boolean.FALSE);
        return x;
    }
    private test() {}
}

命令行

javac test\test.java
scalac useJavaMap.scala
scala useJavaMap
> Map(Test -> false)

很抱歉,这两个都不起作用。这些是泛型类 - Map [String,Boolean]与Map [java.lang.String,java.lang.Boolean]不同,因此您会得到:类型不匹配; 找到:java.lang.Object with scala.collection.jcl.MapWrapper [String,Boolean] {…}需要:Map [String,Boolean](使用第一个示例) - George
还没有时间尝试。提供完整示例。 - jitter
谢谢,Map.empty ++ JMapWrapper[K, V](myJavaMap) 就是我想要的! - Dzmitry Lazerka

0

我认为我有一个部分答案...

如果你使用Java类型将Java Map转换为Scala Map。然后,您可以将其映射到Scala类型的Scala Map:

val javaMap = new java.util.TreeMap[java.lang.String, java.lang.Boolean]
val temp = new collection.jcl.MapWrapper[java.lang.String,java.lang.Boolean] {
    override def underlying = javaMap
}
val scalaMap = temp.map{
    case (k, v) => (k.asInstanceOf[String] -> v.asInstanceOf[Boolean])
}

这个计划的缺陷在于scalaMap的类型是 Iterable[(java.lang.String, Boolean)] 而不是一个 map。我感觉离成功很近了,有没有比我更聪明的人可以修复最后一句话让它正常工作?!

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