Scala-20:Spark实现WordCount案例
一、案例分析
对于一个文件,文件内容是
hello
hello world
hello scala
hello spark from scala
hello flink from scala
现在要统计每个单词出现的次数,采集到控制台
要求:
使用Spark采集
1:建立和Spark框架的连接
2:执行业务操作
-
获取每一行的数据,对每一行的数据进行切分,获得每一个单词
hello world => hello, world
-
对每一个单词进行分组,相同的单词分到一组
RDD[String] => RDD[String, Iterator[String]] = (hello => (hello,hello,hello,hello,hello))
-
对分组之后的list取长度,转换为单词加上出现的次数
RDD[String, Iterator[String]] => RDD[String, Int] = (hello, 5)
-
采集到控制台
3:关闭连接
二、所用方法
1:建立与Spark的连接
val sparkConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(sparkConf)
2:转换/映射方法 map()
将集合中的每一个元素映射到某一个函数
RDD.map()
3:切分字符串 split()
//按照空格切分
RDD.map(_.split(" "))
4:扁平化处理 flatten()
RDD.flatten()
5:扁平化+映射 flatMap()
RDD.flatMap()
6:采集到控制台 collect()
RDD.collect()
三、代码实现
注意:在使用spark之前,要准备spark的开发环境,引入spark的依赖
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
整体代码
def main(args: Array[String]): Unit = {
// Application
// Spark框架
//TODO 建立和Spark框架的连接
val sparkConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(sparkConf)
//TODO 执行业务操作
//1.读取文件,获取一行一行数据
// hello world
val lines: RDD[String] = sc.textFile("datas")
//2.一行一行数据拆分,形成一个一个的单词
// “hello world ” ==> hello world
// 扁平化处理:将整体拆分为个体的操作
val word: RDD[String] = lines.flatMap(_.split(" "))
//3.相同的单词放在一起,将数据根据单词分组,便于统计
// (hello, hello, hello, hello) (world,world)
val wordGroup: RDD[(String, Iterable[String])] = word.groupBy(word => word)
//4.对分组后的数据进行转换
// (hello, hello, hello, hello) ==> (hello, 4)
val wordCount: RDD[(String, Int)] = wordGroup.map {
case (word, list) => {
(word, list.size)
}
}
//5. 将转换的结果采集到控制台打印
val array: Array[(String, Int)] = wordCount.collect()
array.foreach(println)
//TODO 关闭连接
sc.stop()
}
四、代码改进
其实上面的处理方法和之前的WordCount案例相似,这种写法的最后统计阶段并不能完全的说成是聚合过程,真正的实现聚合过程应该在拆分的时候预统计结果,为每一个单词后面增加一个次数 1 ,(hello, 1), (world, 1) ,通过这种方式进行传递,在最后reduce次数1的值才是真正的聚合过程
那么就需要在第二步的拆分操作后,增加一个步骤,为每一个单词后面增加一个次数1
//2.5 在每个单词的后面加上1 (hello, 1)
val wordToOne: RDD[(String, Int)] = word.map(
word => (word, 1)
)
整体代码
def main(args: Array[String]): Unit = {
// Application
// Spark框架
//TODO 建立和Spark框架的连接
val sparkConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(sparkConf)
//TODO 执行业务操作
//1.读取文件,获取一行一行数据
// hello world
val lines: RDD[String] = sc.textFile("datas")
//2.一行一行数据拆分,形成一个一个的单词
// “hello world ” ==> hello world
// 扁平化处理:将整体拆分为个体的操作
val word: RDD[String] = lines.flatMap(_.split(" "))
//2.5 在每个单词的后面加上1 (hello, 1)
val wordToOne: RDD[(String, Int)] = word.map(
word => (word, 1)
)
//3.相同的单词放在一起,将数据根据单词分组,便于统计
// (hello, hello, hello, hello) (world,world)
// (hello,((hello, 1), (hello, 1)))
val wordGroup: RDD[(String, Iterable[(String, Int)])] = wordToOne.groupBy(
t => t._1
)
//4.对分组后的数据进行转换
// (hello, hello, hello, hello) ==> (hello, 4)
val wordCount: RDD[(String, Int)] = wordGroup.map {
case (word, list) => {
list.reduce(
(t1, t2) => {
//reduce的操作,传入的是前后两个 t1 =>(hello, 1) t2 =>(hello, 1)
(t1._1, t1._2 + t2._2)
}
)
}
}
//5. 将转换的结果采集到控制台打印
val array: Array[(String, Int)] = wordCount.collect()
array.foreach(println)
//TODO 关闭连接
sc.stop()
}
五、借用Spark框架实现
Spark框架对于这种操作提供了简介的做法,而我们没有使用到,在3、4步骤,主要就是将单词分组,按照给定的单词作为key,然后对分组的数据进行转换聚合,其实在Sqark框架中,已经提供了这种方法,将两步融合成了一步,我们就可以借助这个方法
按照key进行聚合
reduceByKey()
这样就可以将3、4步骤用reduceByKey实现
val wordCount: RDD[(String, Int)] = wordToOne.reduceByKey(_ + _)
这个简写如果看不懂可以拆分来写
val wordCount: RDD[(String, Int)] = wordToOne.reduceByKey((x, y) => { x + y})
// ====>
val wordCount: RDD[(String, Int)] = wordToOne.reduceByKey((x, y) => x + y)
// ====>
val wordCount: RDD[(String, Int)] = wordToOne.reduceByKey(_ + _)
整体代码
def main(args: Array[String]): Unit = {
// Application
// Spark框架
//TODO 建立和Spark框架的连接
val sparkConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(sparkConf)
//TODO 执行业务操作
//1.读取文件,获取一行一行数据
// hello world
val lines: RDD[String] = sc.textFile("datas")
//2.一行一行数据拆分,形成一个一个的单词
// “hello world ” ==> hello world
// 扁平化处理:将整体拆分为个体的操作
val word: RDD[String] = lines.flatMap(_.split(" "))
//2.5 在每个单词的后面加上1 (hello, 1)
val wordToOne: RDD[(String, Int)] = word.map(
word => (word, 1)
)
//3.相同的单词放在一起,将数据根据单词分组,便于统计
// (hello, hello, hello, hello) (world,world)
// (hello,((hello, 1), (hello, 1)))
//Spark框架提供了更多的功能,可以将分组和聚合使用一个方法实现
//reduceByKey: 相同的key的数据,可以对value进行reduce聚合
//4.对分组后的数据进行转换
val wordCount: RDD[(String, Int)] = wordToOne.reduceByKey(_ + _)
//5. 将转换的结果采集到控制台打印
val array: Array[(String, Int)] = wordCount.collect()
array.foreach(println)
//TODO 关闭连接
sc.stop()
}