学了大半年的数据开发,到今天才发现对wordcount的理解还不是很深刻,本来想在笔记上写一些心得发现太麻烦,于是在csdn上面开通博客,写给自己,方便查看。
首先是代码:
object Test4 {
def main(args: Array[String]): Unit = {
val conf=new SparkConf()
conf.setAppName(s"${this.getClass.getSimpleName}").setMaster("local")
val sc=new SparkContext(conf)
val rdd=sc.parallelize(List("a","b","a","d"))
rdd.map(t=>(t,1)).reduceByKey(_+_).collect().foreach{println}
}
}
除去spark的入口不谈,wordcount有两个最主要的方法就是map和reduceByKey。
map方法:
def map[U: ClassTag](f: T => U): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
}
我们调用 map 时,会传入匿名函数 f: T => U,该函数将一个类型 T 实例转换成一个类型 U 的实例。
map里就是写一个匿名函数,比如:t=>(t+1),t=>(t,1),line=>(.....),类似的,可以将元素t转换成你需要的任何形式,注意如果map()的括号里语句比较复杂,就要写成{},否则会报错。此处还可以用占位符的方式简化,如:t=>(t+1)可以简化成(_+1),同理例子中的t=>(t,1)可以简化成(_,1)。初学scala的时候不理解占位符的用法,浪费了很长时间。
原始数据为 a b c d , 这里经过map(t=>(t,1))后,数据就会变成(a,1) (b,1) (a,1) (d,1)。
再看reduceBykey这个算子:
def reduceByKey(func: (V, V) => V): RDD[(K, V)] = self.withScope {
reduceByKey(defaultPartitioner(self), func)
}
重大误解:我本以为(v,v)是原始键值对,经过方法变为(k,v),实际上这里的(v,v)代表相同的k的两个不同的value值,所以人家写的是两个v,这里的逻辑是两个不同的v进行函数处理,形成一个(k,新v)的一个键值对,至于是什么函数,那就要自己来实现了。
这里的reduceByKey(_+_)就是对于每一个相同的key,让他们的值相加。于是数据从(a,1) (b,1) (a,1) (d,1)=>(a,2) (b,1) (d,1)
这样就实现了单词计数。
gruopByKey()方法:
def groupByKey(): RDD[(K, Iterable[V])] = self.withScope {
groupByKey(defaultPartitioner(self))
}
groupByKey()是一个无参的函数,返回的是一个(k,v的迭代器)。