使用Actor实现WordCount
需求:
用actor并发编程写一个单机版的WordCount,将多个文件作为输入,计算完成后将多个任务汇总,得到最终的结果。
大致的思想步骤:
- 通过loop +react 方式去不断的接受消息
- 利用case class样例类去匹配对应的操作
- 其中scala中提供了文件读取的接口Source,通过调用其fromFile方法去获取文件内容
- 将每个文件的单词数量进行局部汇总,存放在一个ListBuffer中
- 最后将ListBuffer中的结果进行全局汇总。
主要用到的知识点:
通过样例类来接收消息和返回消息
例:case class ResultTask(result: Map[String, Int])flatMap函数 实现分割后压扁
例:lines.flatMap(x => x.split(" "))map函数初始化次数,构建元组
例:words.map(word => (word, 1))groupBy函数进行分组
例:wordAndOne.groupBy(x => x._1)mapValues函数进行相同的key次数统计
例:groupByWord.mapValues(x => x.length)filter函数过滤有效值
例:futureSet.filter(x => x.isSet)类型转换
例:value.asInstanceOf[ResultTask]foldLeft函数求和计算
例:x.foldLeft(0)((x, y) => x + y._2)
具体代码如下:
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.Source
case class SubmitTask(fileName: String)
case class ResultTask(result: Map[String, Int])
//todo:通过scala中actor来实现多个文件单词统计的结果
class Task extends Actor {
override def act(): Unit = {
loop {
react {
case SubmitTask(fileName) => {
//1.读取文件内容
val data: String = Source.fromFile(fileName).mkString
println(data)
//运行结果:
//hello dog
//cat small
//2.按照换行符进行切分 windows下换行符\r\n linux下换行符是\n mac是\r
val lines: Array[String] = data.split("\r\n")
println(lines.toBuffer)
//运行结果:ArrayBuffer(hello dog, cat small)
//3.切分每一行获取所有的单词
val words: Array[String] = lines.flatMap(x => x.split(" "))
println(words.toBuffer)
//运行结果:ArrayBuffer(hello, dog, cat, small) 这里使用flatMap是分割后再压扁
//4.每个单词计数为1
val wordAndOne: Array[(String, Int)] = words.map(word => (word, 1))
println(wordAndOne.toBuffer)
//运行结果:ArrayBuffer((hello,1), (dog,1), (cat,1), (small,1))
//5.按照单词进行分组
val groupByWord: Map[String, Array[(String, Int)]] = wordAndOne.groupBy(x => x._1)
for ((k, v) <- groupByWord) println(k + " -> " + v.toBuffer)
//运行结果:
//cat -> ArrayBuffer((cat,1))
//dog -> ArrayBuffer((dog,1))
//small -> ArrayBuffer((small,1))
//hello -> ArrayBuffer((hello,1))
//6.统计单词出现的次数
val result: Map[String, Int] = groupByWord.mapValues(x => x.length)
println(result)
//运行结果:
//Map(cat -> 1, dog -> 1, small -> 1, hello -> 1)
//7.通过sender把结果数据返回回去
sender ! ResultTask(result)
}
}
}
}
}
object WordCount {
def main(args: Array[String]): Unit = {
//定义一个Set集合,用于保存每一个文件所产生的Future
val futureSet: mutable.HashSet[Future[Any]] = new mutable.HashSet[Future[Any]]()
//定义一个list集合,用户保存每一个future中的结果数据
val resultList: ListBuffer[ResultTask] = new ListBuffer[ResultTask]
//准备数据文件
val files = Array(
"E:\\aa.txt",
"E:\\bb.txt",
"E:\\cc.txt"
)
for (f <- files) {
//1.创建
val task = new Task
//2.启动
task.start()
//3.发送
val reply: Future[Any] = task !! SubmitTask(f)
//4.把每一个future加入到Set集合中
futureSet += reply
}
//遍历
while (futureSet.size > 0) { //此时size为3
//过滤出有真正结果数据的future
val set: mutable.HashSet[Future[Any]] = futureSet.filter(x => x.isSet) //拿到已经有数据的future
//遍历集合,获取到每一个有真正数据的future
for (c <- set) {
val value: Any = c.apply()
resultList += value.asInstanceOf[ResultTask]
//从futureSet移除掉 取出的future
futureSet -= c
}
}
//println(resultList.map(x=>x.result).flatten)
println(resultList.flatMap(x => x.result).groupBy(x => x._1))
//运行结果:Map(dog -> ListBuffer((dog,1)), cat -> ListBuffer((cat,1)), small -> ListBuffer((small,1)), hello -> ListBuffer((hello,1)))
println(resultList.flatMap(x => x.result).groupBy(x => x._1)
.mapValues(x => x.foldLeft(0)((x, y) => x + y._2)))
//运行结果:Map(dog -> 1, cat -> 1, small -> 1, hello -> 1)
}
}