13

我最近偶然在这个post,其中“介绍”为Scala集合的collect方法。用法是直截了当:使用收集的地图,斯卡拉

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

现在地图基本上都是键值对,这是由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 } 

编译器当然抱怨由于JVM的类型擦除模式,所以我们尝试的第一件事就是使用存在类型:

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

虽然代码通过了编译器,结果是“正确的”(我们希望pair =>我们得到了对),但我们仍然没有得到我们想要的东西。第二次尝试是这样的:

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) 

好了,我们几乎没有:

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

我们做到了,但不是从(Any,Any)只铸造(Int,String)我们实际复制每一对,从而创造了一个新对。

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

所以这是我的问题。

与所有的检查,铸件和映射,我们成功地转移到Map[Any,Any]Map[String,Int]。有没有更好的方法来做到这一点?我的意思是类型有,JVM只是没有看到他们(据我所关注的)...

回答

21
pairs collect { case p @ (_: Int, _: String) => p.asInstanceOf[(Int, String)] } 

或更简洁,但也有一些开销,我认为

pairs collect { case (x: Int, y: String) => (x, y) } 
+2

酷THX !我知道Scala是足够真棒做到这一点;) – agilesteel

+2

哦喜悦!新的特殊字符(@),我几乎猜到了。 – Ciantic