Как конвертировать из 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)], а не карта. Я чувствую себя так близко, может ли кто-нибудь умнее меня исправить последнее выражение, чтобы сделать эту работу?!