scala模式匹配的一个问题
Posted 后端技术小黑屋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了scala模式匹配的一个问题相关的知识,希望对你有一定的参考价值。
最近开始用scala做一些工作。
scala和java可以说同源同种,而我上一次写java程序可能要追溯到快十年前了。平常习惯使用弱类型语言,突然间切到scala,还有点不太适应。最近碰到的这个小问题,就耗费掉我不少时间。
大概场景是有两份数据left和right,需要对他们做一次全连接(fullOuterJoin),然后对于得到的结果,优先选择left中的数据,left中的数据不存在则选择right中的数据。示意如下:
scala> val left = sc.makeRDD(Seq(("1","2","LEFT12"),("2","3","LEFT23")))
left: org.apache.spark.rdd.RDD[(String, String, String)] = ParallelCollectionRDD[0] at makeRDD at <console>:24
scala> val right = sc.makeRDD(Seq(("1","2","RIGHT12"),("3","4","RIGHT34")))
right: org.apache.spark.rdd.RDD[(String, String, String)] = ParallelCollectionRDD[1] at makeRDD at <console>:24
scala> val leftmap = left.map(x => (x._1, x._2) -> x._3)
leftmap: org.apache.spark.rdd.RDD[((String, String), String)] = MapPartitionsRDD[2] at map at <console>:26
scala> val rightmap = right.map(x => (x._1, x._2) -> x._3)
rightmap: org.apache.spark.rdd.RDD[((String, String), String)] = MapPartitionsRDD[3] at map at <console>:26
接下进行fullOuterJoin,查看文档后得到,fullOuterJoin返回的结果是两个Option组成的元组。感谢scala的模式匹配,可以避免写一大堆if else,以下代码看上去很美好:
scala> val total = leftmap.fullOuterJoin(rightmap).map(kv =>
| kv._2 match {
| case (Some(s), None) => s
| case (None, Some(s)) => s
| case (Some(s), Some(t)) => s
| case _ => None
| })
total: org.apache.spark.rdd.RDD[java.io.Serializable] = MapPartitionsRDD[11] at map at <console>:32
scala> total.collect()
res3: Array[java.io.Serializable] = Array(RIGHT34, LEFT12, LEFT23)
但是返回值java.io.Serializable是什么鬼?这类型和我后续要把数据落地的接口不匹配,后续工作无法进行啊。
翻看过各种Stack Overflow上的问答,各种java.io.serializable的搜索结果,最终才弄明白,原因是scala的模式匹配语句match的返回值类型是各个case字句返回值类型的最近公共父类。这里的问题就出在case _ => None这一句,None类型和s的类型,导致最终scala找到java.io.serializable。
再做个试验验证下,比如这样的模式匹配:
scala> val total = leftmap.fullOuterJoin(rightmap).map(kv =>
| kv._2 match {
| case (Some(s), None) => 1
| case (None, Some(s)) => 2
| case (Some(s), Some(t)) => 1
| case _ => ""
| })
返回类型将是整型和字符串类型的公共父类Any类型:
total: org.apache.spark.rdd.RDD[Any] = MapPartitionsRDD[19] at map at <console>:32
解决方案也很简单,既然我知道我要的就是字符串,那么最后一个case子句返回空字符串:
scala> val total = leftmap.fullOuterJoin(rightmap).map(kv =>
| kv._2 match {
| case (Some(s), None) => s
| case (None, Some(s)) => s
| case (Some(s), Some(t)) => s
| case _ => ""
| })
total: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[15] at map at <console>:32
scala> total.collect()
res4: Array[String] = Array(RIGHT34, LEFT12, LEFT23)
对于scala/spark,我还是萌新一枚,有什么说的不准确的地方,也欢迎各位大佬留言批评指正~
推荐阅读:
题图:geralt
授权:CC0协议
以上是关于scala模式匹配的一个问题的主要内容,如果未能解决你的问题,请参考以下文章