单词count统计这个经典的问题可以算得上是Spark大数据统计的 hello world问题,但是,打蚊子何需动用Spark这门大炮。鉴于Spark是基于Scala的,我们就用Scala本身来实现这个经典的问题,从而达到举一反三的目的。
val str = "hello world,Hello scala"
val split1 = str.split(" ")
var split2 = split1.flatMap(_.split(","))
val res = split2.map(_.toLowerCase()).map((_,1)).groupBy(_._1).map(kv=>(kv._1,kv._2.size))
res.foreach(println)
因为字符串中包含两种分隔符,所以先用空格符进行第一次分割,这时split1是一个string类型的java原生数组,然后再用逗号进行第二次分割,这时需要注意,如果用普通的map去循环分割,最后split2会得到一个二位数组,增加计算的复杂度,所以flatMap上场了,分割后仍然是一个一维数组,相当于在原基础上把数组压平了,后面会对flatMap进行重新实现,以便对其的作用有一个更直观的理解。
这时的split2看起来是这个样子的
List(hello,world,Hello,scala)
因为还涉及到大小写,所以对其进行小写转换,再转换成对偶元祖,经过两次map操作后,这是的临时数据看起来是这样的
List((hello,1),(world,1),(hello,1),(scala,1))
然后使用groupBy进行分组,_._1表示进行归并分组,最后返回得到一个map,键名用每个元祖的第一个元素
Map(‘scala’->List((scala,1)),’world’->List((world,1)),’hello’->List((hello,1),(hello,1))
最后再进行修饰,只取长度值
Map(‘scala’->1,’world’->1,’hello’->2)
得到我们想要的统计结果。下面是flatMap的实现代码
def flatMap(arr:Array[String]): Array[String] ={
var res = Array[String]()
val temp = arr.map(_.split(","))
temp.foreach((v)=>{
v.foreach(v2=>{res = res :+ v2})
})
res
}
这里只给出了一个简单实现,系统的实现比这要复杂很多,读者可自行阅读SDK里的实现源码
附:(请一定要看完)
经过思考发现,groupBy只要传入一个函数参数即可对系列进行分组,那么为什么一定要把List的元素转化为元组再进行groupBy呢?直接groupBy可以不可以,其实是可以的。代码如下
split2.map(_.toLowerCase()).groupBy(v=>v).map(v=>(v._1,v._2.size))
这是精简后的代码,直接使用元素本身来进行分组,值得注意的是,这里的groupBy没法简写成groupBy(_),但是却可以写成这样
groupBy(_*1)
目测简写为下划线后还需要跟其他操作符(点操作符或者四则运算),不过具体细节需要阅读源码才能知晓其中的微妙了
网上针对这个例子大多是进行了元组转换操作,其实大可不必,而且也不太好理解,可见独立的深度思考非常重要,不可人云亦云
最后强烈建议新手不要随意使用简写形式,先按常规形式写全函数表达式,在可能的基础上再进行缩写,否则陡峭的Scala学习曲线会让你挫败感顿生。
(完结)

















近期评论