A Java API returns a java.util.Map<java.lang.String,java.lang.Boolean>
;. I would like to put that into a Map[String,Boolean]
Java API返回java.util.Map
So imagine we have:
所以想象我们有:
var scalaMap : Map[String,Boolean] = Map.empty
val javaMap = new JavaClass().map() // Returns java.util.Map<java.lang.String,java.lang.Boolean>
You can't do Map.empty ++ javaMap
, because the ++ method does not know about Java maps. I tried:
你不能做Map.empty ++ javaMap,因为++方法不了解Java地图。我试过了:
scalaMap = Map.empty ++ new collection.jcl.MapWrapper[String,Boolean] {
override def underlying = javaMap
}
and:
scalaMap = Map.empty ++ new collection.jcl.MapWrapper[java.lang.String,java.lang.Boolean] {
override def underlying = javaMap
}
These both fail to compile, because of the generics - java.lang.String
is not the same as a scala String.
这两个都无法编译,因为泛型 - java.lang.String与scala String不同。
Is there a good way of doing this, short of copying the map manually?
有没有一种好方法可以做到这一点,而不是手动复制地图?
EDIT: Thanks, all good answers, I learned a lot from all of them. However, I made a mistake by posting a simpler problem here than the one I actually have. So, if you allow me, I'll generalise the question - What the API actually returns is
编辑:谢谢,所有好的答案,我从他们所有人那里学到了很多东西。但是,我在这里发布一个比我实际拥有的更简单的问题犯了一个错误。所以,如果你允许我,我会概括一个问题 - API实际返回的是什么
java.util.Map<java.lang.String, java.util.Map<SomeJavaEnum,java.lang.String>>
And I need to move this to Map[String, Map[SomeJavaEnum,String]]
我需要将其移动到Map [String,Map [SomeJavaEnum,String]]
It probably does not seem like too much of a complication, but it adds an extra level of type erasure, and the only way I found of moving this to a Scala map was deep-copying it (using some of the techniques you suggested below). Anyone any hints? I kind of solved my problem by defining an implicit conversion for my exact types, so at least the ugliness is hidden in its own trait, but still feels a bit clumsy deep copying the lot.
它似乎不太复杂,但它增加了额外级别的类型擦除,我发现将其移动到Scala地图的唯一方法是深度复制它(使用下面建议的一些技术) 。任何提示?我通过为我的确切类型定义一个隐式转换来解决我的问题,所以至少丑陋隐藏在它自己的特性中,但仍然感觉有点笨拙的深层复制。
4 个解决方案
#1
At least with Scala 2.9.2 there's an easier way with the collections conversions: import "import collection.JavaConversions._" and use "toMap".
至少使用Scala 2.9.2,集合转换更简单:导入“import collection.JavaConversions._”并使用“toMap”。
Example:
// 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)
It works fine with a java.util.map of String to Boolean.
使用String的java.util.map到Boolean可以正常工作。
#2
A Scala String
is a java.lang.String
but a Scala Boolean
is not a java.lang.Boolean
. Hence the following works:
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
But your problem is still the issue with the Boolean
difference. You'll have to "fold" the Java map into the scala one: try again using the Scala Boolean
type:
但是你的问题仍然是布尔差异的问题。您必须将Java地图“折叠”到scala中:使用Scala布尔类型再次尝试:
val sm: MMap[String, Boolean] = collection.mutable.Map.empty + ("WORLD" -> false)
val mm = (sm /: jm) { (s, t2) => s + (t2._1 -> t2._2.booleanValue) }
Then mm
is a scala map containing the contents of the original scala map plus what was in the Java map
然后mm是一个scala地图,其中包含原始scala地图的内容以及Java地图中的内容
#3
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() {}
}
Commandline
javac test\test.java
scalac useJavaMap.scala
scala useJavaMap
> Map(Test -> false)
#4
I think I have a partial answer...
我想我有部分答案......
If you convert the java map to a scala map with the java types. You can then map it to a scala map of scala types:
如果将java映射转换为带有java类型的scala映射。然后,您可以将其映射到scala类型的scala映射:
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])
}
The flaw in this plan is that the type of scalaMap
is Iterable[(java.lang.String, Boolean)] not a map. I feel so close, can someone cleverer than me fix the last statement to make this work?!
这个计划的缺陷是scalaMap的类型是Iterable [(java.lang.String,Boolean)]而不是map。我觉得如此接近,有人比我更聪明地修复最后的声明来使这项工作?!
#1
At least with Scala 2.9.2 there's an easier way with the collections conversions: import "import collection.JavaConversions._" and use "toMap".
至少使用Scala 2.9.2,集合转换更简单:导入“import collection.JavaConversions._”并使用“toMap”。
Example:
// 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)
It works fine with a java.util.map of String to Boolean.
使用String的java.util.map到Boolean可以正常工作。
#2
A Scala String
is a java.lang.String
but a Scala Boolean
is not a java.lang.Boolean
. Hence the following works:
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
But your problem is still the issue with the Boolean
difference. You'll have to "fold" the Java map into the scala one: try again using the Scala Boolean
type:
但是你的问题仍然是布尔差异的问题。您必须将Java地图“折叠”到scala中:使用Scala布尔类型再次尝试:
val sm: MMap[String, Boolean] = collection.mutable.Map.empty + ("WORLD" -> false)
val mm = (sm /: jm) { (s, t2) => s + (t2._1 -> t2._2.booleanValue) }
Then mm
is a scala map containing the contents of the original scala map plus what was in the Java map
然后mm是一个scala地图,其中包含原始scala地图的内容以及Java地图中的内容
#3
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() {}
}
Commandline
javac test\test.java
scalac useJavaMap.scala
scala useJavaMap
> Map(Test -> false)
#4
I think I have a partial answer...
我想我有部分答案......
If you convert the java map to a scala map with the java types. You can then map it to a scala map of scala types:
如果将java映射转换为带有java类型的scala映射。然后,您可以将其映射到scala类型的scala映射:
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])
}
The flaw in this plan is that the type of scalaMap
is Iterable[(java.lang.String, Boolean)] not a map. I feel so close, can someone cleverer than me fix the last statement to make this work?!
这个计划的缺陷是scalaMap的类型是Iterable [(java.lang.String,Boolean)]而不是map。我觉得如此接近,有人比我更聪明地修复最后的声明来使这项工作?!