在Scala中使用地图上的收集

agi*_*eel 13 compiler-construction jvm scala type-erasure scala-collections

我最近偶然发现了这篇文章,它"介绍"了collectScala集合的方法.用法很简单:

scala> val ints = List(1, "2", 3) collect { case i: Int => i }
ints: List[Int] = List(1, 3)
Run Code Online (Sandbox Code Playgroud)

现在,映射基本上是键值对的列表,它们由Scala中的元组表示.所以你可能想尝试这样的事情:

scala> val pairs = Map(1 -> "I", "II" -> 2)
pairs: scala.collection.immutable.Map[Any,Any] = Map(1 -> I, II -> 2)

scala> val intsToStrings = pairs collect { case pair: (Int, String) => pair }
Run Code Online (Sandbox Code Playgroud)

编译器当然抱怨由于JVM的类型擦除模型,所以我们首先尝试使用存在类型:

scala> val intsToStrings = pairs collect { case pair: (_, _) => pair }
intsToString: scala.collection.immutable.Map[Any,Any] = Map(1 -> I, II -> 2)
Run Code Online (Sandbox Code Playgroud)

虽然代码传递了编译器,结果是"正确的"(我们想要对=>我们得到了对),但我们仍然没有得到我们真正想要的东西.第二次尝试看起来像这样:

scala> val intsToStrings = pairs collect {
     |    case pair: (_, _) if pair._1.isInstanceOf[Int] && pair._2.isInstanceOf[String] => pair
     | }
intsToStrings: scala.collection.immutable.Map[Any,Any] = Map(1 -> I)
Run Code Online (Sandbox Code Playgroud)

好的,我们几乎在那里:

scala> val realIntsToRealStrings = intsToStrings map {
     |    pair => (pair._1.asInstanceOf[Int], pair._2.asInstanceOf[String])
     | }
realIntsToRealStrings: scala.collection.immutable.Map[Int,String] = Map(1 -> I)
Run Code Online (Sandbox Code Playgroud)

我们做到了,但是我们实际上复制了每一对,而不是仅仅从中(Any,Any)转换(Int,String),从而创建了一对新对.

现在是问题部分.正如我提到的"编译器当然抱怨......"我听起来好像我真的知道我在说什么.我不!我所知道的是Java从一开始就没有泛型.在某些时候,泛型进入Java而不进入JVM.因此编译器会检查所有类型,但是一旦代码运行,JVM就不关心参数类型.它只看到它是a Map或a List而不是它是a Map[String, Int]或者List[Int].

所以这是我的问题.

通过所有检查,转换和映射,我们设法转移Map[Any,Any]Map[String,Int].有没有更好的方法呢?我的意思是类型在那里,JVM只是看不到它们(据我所知)...

inc*_*rop 22

pairs collect { case p @ (_: Int, _: String) => p.asInstanceOf[(Int, String)] }
Run Code Online (Sandbox Code Playgroud)

或者更简洁,但有一些开销,我想

pairs collect { case (x: Int, y: String) => (x, y) }
Run Code Online (Sandbox Code Playgroud)

  • 哦,快乐!新的特殊字符(@),我差点猜到了. (2认同)