Как конвертировать из java.util.Map в карту Scala
API Java возвращает 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. Я пробовал:
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.
Есть ли хороший способ сделать это, не скопировав карту вручную?
EDIT: Спасибо, все хорошие ответы, я многому научился у всех из них. Однако я допустил ошибку, разместив здесь более простую проблему, чем тот, который у меня есть. Итак, если вы позволите мне, я обобщу вопрос: что API API действительно возвращает
java.util.Map<java.lang.String, java.util.Map<SomeJavaEnum,java.lang.String>>
И мне нужно переместить это в Map [String, Map [SomeJavaEnum, String]]
Вероятно, это не слишком усложняется, но добавляет дополнительный уровень стирания типа, и единственный способ, которым я нашел перемещение на карту scala, - это глубокое копирование (используя некоторые из методы, предложенные ниже). Любой намек? Я как бы решил проблему, определив неявное преобразование для своих точных типов, поэтому, по крайней мере, уродство скрыто в его собственном признаке, но все же чувствует себя немного неуклюжим, глубоко копируя лот.
Ответы
Ответ 1
По крайней мере, с 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 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)
Он отлично работает с java.util.map строки для Boolean.
Ответ 2
A Scala String
является java.lang.String
, но a Scala Boolean
не a 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 в Scala: повторите попытку, используя тип 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, содержащая содержимое исходной карты 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.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
Я думаю, что у меня есть частичный ответ...
Если вы преобразуете карту java в карту scala с помощью типов java. Затем вы можете сопоставить его с картой 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])
}
Недостатком этого плана является то, что тип scalaMap
- это Iterable [(java.lang.String, Boolean)], а не карта. Я чувствую себя так близко, может ли кто-нибудь умнее меня исправить последнее выражение, чтобы сделать эту работу?!