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

发布于 2024-07-25 08:29:49 字数 1284 浏览 3 评论 0原文

Java API 返回 java.util.Map;。 我想将其放入 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 String 不同。

除了手动复制地图之外,有没有一种好方法可以做到这一点?

编辑:谢谢,所有好的答案,我从他们身上学到了很多东西。 然而,我犯了一个错误,在这里发布了一个比我实际遇到的问题更简单的问题。 所以,如果你允许的话,我会概括这个问题 - API 实际返回的是

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

我需要将其移至 Map[String, Map[SomeJavaEnum,String]]

它可能看起来不太复杂,但它增加了额外级别的类型擦除,我发现将其移动到 Scala 映射的唯一方法是深度复制它(使用您在下面建议的一些技术)。 有人有任何提示吗? 我通过为我的确切类型定义隐式转换来解决我的问题,所以至少丑陋隐藏在它自己的特征中,但仍然感觉有点笨拙的深度复制很多。

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]

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:

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.

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

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]]

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

悲欢浪云 2024-08-01 08:29:50

至少在 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 配合使用。

At least with Scala 2.9.2 there's an easier way with the collections conversions: import "import collection.JavaConversions._" and use "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.

黒涩兲箜 2024-08-01 08:29:50

Scala Stringjava.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 映射“折叠”到 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 映射中的内容

A Scala String is a java.lang.String but a Scala Boolean is not a java.lang.Boolean. Hence the following works:

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:

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

有木有妳兜一样 2024-08-01 08:29:50

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)

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)
洛阳烟雨空心柳 2024-08-01 08:29:50

我想我有一个部分答案......

如果你将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])
}

该计划的缺陷是 scalaMap 的类型是 Iterable[(java.lang.String, Boolean)] 而不是映射。 我感觉很接近,有比我聪明的人可以修改最后一个语句来使这个工作成功吗?!

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:

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?!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文