使用Scala实现Java项目的单词计数:串行及Actor版本
其实我想找一门“具有Python的简洁写法和融合Java平台的优势, 同时又足够有挑战性和灵活性”的编程语言。 Scala 就是一个不错的选择。 Scala 有很多语言特性, 建议先掌握基础常用的: 变量、控制结构 、正则与模式匹配、集合、文件读写/目录遍历、高阶函数、并发 Actor 模型; 然后是面向对象特性:类、Trait、泛型、注解 、操作符重载; 最后再细细学习那些复杂不常用的特性:类型转换、编译解析等;注重挖掘根源性的思想,能够推导出其它的特性。
本文使用 Scala 实现 Java 项目的单词计数,采用了顺序编程模型; 后面给出了 Actor 模型的基本实现。
Scala 常用特性:
1. 与 Java 库方便地互操作;能够使用在 Java Maven 项目中, 只要配置好相应的 maven 依赖包和插件即可;
2. 不必写分号,脚本特性; 如果多个语句在同一行,则必须写分号加以区分;
3. 变量声明为 var, 不变量声明为 val ; 优先考虑不变性;
4. 属性在前,类型在后; “先思考数据模型,后确定数据类型” 的思想;
5. 静态类型,具备类型推导; 既有工程特性,又兼具脚本能力;
6. 函数无返回值定义为 Unit , “通吃型”类型 定义为 Any: var typechangeable: Any ; 后续可赋为数值,亦可赋值为字符串或集合;
7. 无参函数调用可以只写函数名,不必写();
8. 匿名函数可以写成 (A,B,...,Z) => exe(A,B,...,Z) ; 函数式编程风格有种数学的严谨和优雅;
9. 常用集合: Array, ArrayBuffer, mutable.List, immutable.List, mutable.Map, mutable.HashMap, immutable.HashMap, Tuple,
mutable.Set, immutable.Set, mutable.HashSet, immutable.HashSet 集合有很多方便的工具和方法可使用,可相互转化;
访问 Array 或 Map , 使用 array(0) = "xxx" 或 map("key") = "value" ; 访问 Tuple 使用 ._1, ._2, ... 第一个索引为 1 !
9. 使用集合时必须先指明是 mutable 还是 immutable ; 一般函数返回值使用 immutable, 局部变量使用 mutable ;
10. 集合添加元素使用 += , 集合连接集合使用 ++= ;
11. 使用 map, filter 方法对集合映射或过滤处理, 传递给函数的参数名为 _ ; 组合起来很强大!
12. 使用 collection.foreach { e => exe(e) } 或 for(e <- collection) { exe(e) } 进行遍历处理;我更喜欢第一种写法;
13. 使用 object YourAPP extends App { //code } 实现 Main 函数,直接在块 //code 里写主流程,就像 Python 一样;
14. import , 嵌套类、函数、trait 等可以出现的地方很灵活;
15. class MyClass(f1: FType, f2:FType2) extends XXX 可定义类的构造器,相当于声明了属性 f1, f2 的 JavaBean;
16. 使用类的伴生对象来计数和一些操作; 类的伴生对象用于存储类的单例以及静态成员及静态方法;
17. 使用 actor ! messageString 或 actor ! object 向 actor 发送信息,就好比使用 obj.dosome 要求对象执行某动作一样;
18. 若要发送的消息比较复杂,可以包装成一个类。 case class MessageWrapper(params ...) { } ; 使用 case MessageWrapper(params ...) => 来接收和使用该类型的消息。
19. 多看文档,少造轮子。
串行版本
结构很清晰: 获取指定目录的所有 Java 文件 -> 读取所有 java 文件 => 解析出所有单词列表 => 统计所有单词。基本上常见的集合(Array, ArrayBuffer, List, Map, Tuple ) 都用到了。注意到,通过使用 map 方法, 形成连锁调用, 这种风格还是很流畅的~~ WordStat.init 方法主要用于在其它类中调用以初始化 WordStat.seps , 比如后面的 Actor 并发版本。如果只是直接调用 WordStat 的方法, WordStat.seps 会被初始化为 null
package scalastudy.basicimport scala.collection.immutable.List import scala.collection.mutable import scala.collection.mutable.{ArrayBuffer, Map, HashMap} import java.io.File/*** Created by lovesqcc on 16-3-19.*/ object WordStat extends App {var seps = " -!\"#$%&()*,./:;?@[]^_`{|}~+<=>\\".toArraylaunch()def init(): Unit = {if (WordStat.seps == null) {seps = " -!\"#$%&()*,./:;?@[]^_`{|}~+<=>\\".toArray}}def launch(): Unit = {val path = "/home/lovesqcc/work/java/ALLIN/src/main/java/"val files = fetchAllJavaFiles(path)//files.foreach { println } val allWords = files.map(readFile(_)).map(analysisWords(_)).flatten.toListsortByValue(statWords(allWords)).map(println)}def fileJavaFile(filename:String, suffix:String): Boolean = {return filename.endsWith(suffix)}def fetchAllJavaFiles(path:String): Array[String] = {val javaFilesBuf = ArrayBuffer[String]()fetchJavaFiles(path, javaFilesBuf)return javaFilesBuf.toArray}def fetchJavaFiles(path:String, javafiles:ArrayBuffer[String]):Unit = {val dirAndfiles = new File(path).listFilesif (dirAndfiles!=null && dirAndfiles.length > 0) {val files = dirAndfiles.filter(_.isFile)if (files.length > 0) {javafiles ++= files.map(_.getCanonicalPath).filter(fileJavaFile(_,".java"))}val dirs = dirAndfiles.filter(_.isDirectory)if (dirs.length > 0) {dirs.map(_.getCanonicalPath).foreach { dirpath =>fetchJavaFiles(dirpath, javafiles) }}}}def readFile(filename:String): String = {import scala.io.Sourceval fileSource = Source.fromFile(filename)try {return fileSource.mkString} finally {fileSource.close()}}def analysisWords(content:String):List[String] = {return splitText(content, WordStat.seps);}def statWords(words: List[String]):Map[String,Int] = {val wordsMap = new HashMap[String,Int]words.foreach { w =>wordsMap(w) = wordsMap.getOrElse(w,0) + 1}return wordsMap}def splitText(text:String, seps:Array[Char]): List[String] = {var init = Array(text)seps.foreach { sep =>init = init.map(_.split(sep)).flatten.map(_.trim).filter(s => s.length > 0)}return init.toList}def sortByValue(m: Map[String,Int]): Map[String,Int] = {val sortedm = new mutable.LinkedHashMap[String,Int]m.toList.sortWith{case(kv1,kv2) => kv1._2 > kv2._2}.foreach { t =>sortedm(t._1) = t._2}return sortedm}}
初步的 Actor 并发版本
1. 角色分工: 从目录获取 Java 文件的 FetchJavaFileActor ; 读取文件的 ReadFileActor ; 从文件中解析单词的 AnalysisWordActor ; 统计单词数目的 StatWordActor 。 由于在串行版本中已经做到很好的复用,因此在角色分工创建 Actor 时,只需要将相应的函数移进去作为方法调用即可。
2. 每个 Actor 完成自己的工作后,会向下一个 Actor 发送消息,因此前面的 Actor 会持有下一个 Actor 的引用。 FetchJavaFileActor -> ReadFileActor
-> AnalysisWordActor -> StatWordActor
不完善的地方: 1. FetchJavaFileActor 一次性获取所有文件后发送,并发度不高; 2. Actor 终止的方式很简单。 后续改进;3. 消息接收不够健壮。
注: 在 Java Maven 项目中配置可运行 Scala + Akka 程序见后面。
package scalastudy.concurrentimport java.lang.Threadimport akka.actor.{ActorRef, Props, ActorSystem, Actor} import akka.actor.Actor.Receiveimport scala.collection.immutable.List import scala.collection.mutable import scala.collection.mutable.{ArrayBuffer, Map, HashMap} import java.io.Fileimport scalastudy.basic.WordStat/*** Created by lovesqcc on 16-3-19.*/ object ConcurrentWordStat extends App {val seps = " -!\"#$%&()*,./:;?@[]^_`{|}~+<=>\\".toArraylaunch()def launch(): Unit = {val path = "/home/lovesqcc/work/java/ALLIN/src/main/java/"val system = ActorSystem("actor-wordstat")val statWordActor = system.actorOf(Props[StatWordActor])val analysisWordActor = system.actorOf(Props(new AnalysisWordActor(statWordActor)))val readFileActor = system.actorOf(Props(new ReadFileActor(analysisWordActor)))val fetchFileActor = system.actorOf(Props(new FetchJavaFileActor(readFileActor)))fetchFileActor ! pathThread.sleep(6000)val concurrentResult:Map[String,Int] = sortByValue(StatWordActor.finalResult())WordStat.init()val allWords = WordStat.fetchAllJavaFiles(path).map(WordStat.readFile(_)).map(WordStat.analysisWords(_)).flatten.toListval basicResult:Map[String,Int] = sortByValue(WordStat.statWords(allWords))// Compare the results of serial version and actors versionconcurrentResult.keySet.foreach { key =>assert(concurrentResult(key) == basicResult(key))}println("All Passed. Yeah ~~ ")system.shutdown}class FetchJavaFileActor(readFileActor: ActorRef) extends Actor {override def receive: Actor.Receive = {case path:String =>val allJavaFiles:Array[String] = fetchAllJavaFiles(path)allJavaFiles.foreach {readFileActor ! _}}def fileJavaFile(filename:String, suffix:String): Boolean = {return filename.endsWith(suffix)}def fetchAllJavaFiles(path:String): Array[String] = {val javaFilesBuf = ArrayBuffer[String]()fetchJavaFiles(path, javaFilesBuf)return javaFilesBuf.toArray}def fetchJavaFiles(path:String, javafiles:ArrayBuffer[String]):Unit = {val dirAndfiles = new File(path).listFilesif (dirAndfiles!=null && dirAndfiles.length > 0) {val files = dirAndfiles.filter(_.isFile)if (files.length > 0) {javafiles ++= files.map(_.getCanonicalPath).filter(fileJavaFile(_,".java"))}val dirs = dirAndfiles.filter(_.isDirectory)if (dirs.length > 0) {dirs.map(_.getCanonicalPath).foreach { dirpath =>fetchJavaFiles(dirpath, javafiles) }}}}}// 记录读取的文件数便于核对 object ReadFileActor {private var fileCount = 0private def inc() { fileCount +=1 }private def count() = fileCount}class ReadFileActor(analysisWordActor: ActorRef) extends Actor {override def receive: Receive = {case filename:String =>ReadFileActor.inc()println("File count: " + ReadFileActor.count())println(filename)val content = readFile(filename)analysisWordActor ! content}def readFile(filename:String): String = {import scala.io.Sourceval fileSource = Source.fromFile(filename)try {return fileSource.mkString} finally {fileSource.close()}}}case class WordListWrapper(wordlist: List[String]) {def getWordlist = wordlist}class AnalysisWordActor(statWordActor: ActorRef) extends Actor {override def receive: Actor.Receive = {case content:String =>val words = analysisWords(content)statWordActor ! new WordListWrapper(words)}def analysisWords(content:String):List[String] = {return splitText(content, ConcurrentWordStat.seps);}def splitText(text:String, seps:Array[Char]): List[String] = {var init = Array(text)seps.foreach { sep =>init = init.map(_.split(sep)).flatten.map(_.trim).filter(s => s.length > 0)}return init.toList}}object StatWordActor {var stat:Map[String,Int] = new HashMap[String,Int]def add(newstat:Map[String,Int]) = {newstat.foreach { e =>stat(e._1) = stat.getOrElse(e._1, 0) + newstat.getOrElse(e._1, 0)}}def finalResult() = statprivate var recvCount = 0private def inc() { recvCount +=1 }private def count() = recvCount}class StatWordActor extends Actor {override def receive: Actor.Receive = {case WordListWrapper(wordlist: List[String]) =>StatWordActor.inc()println("received times: " + StatWordActor.count())val stat:Map[String,Int] = statWords(wordlist)StatWordActor.add(stat)}def statWords(words: List[String]):Map[String,Int] = {val wordsMap = new HashMap[String,Int]words.foreach { w =>wordsMap(w) = wordsMap.getOrElse(w,0) + 1}return wordsMap}}def sortByValue(m: Map[String,Int]): Map[String,Int] = {val sortedm = new mutable.LinkedHashMap[String,Int]m.toList.sortWith{case(kv1,kv2) => kv1._2 > kv2._2}.foreach { t =>sortedm(t._1) = t._2}return sortedm}}
在 Java Maven 项目中正常运行 Scala + AKKA 编写的程序
主要是 Java 版本 + Scala 版本 + AKKA 版本 三者要兼容,版本关系参考 http://akka.io/downloads/ ;先确定 java 版本,然后确定 akka 版本,最后选择 Scala 版本。 scala-library 的版本 2.11.x 必须与akka artifactId 版本 保持一致 ! 比如 akka artifactId 版本是 <artifactId>akka-actor_2.11</artifactId>,那么 Scala 版本必须是 2.11.x 。
如果是 java8, 那么 jdk8 + <scala-library2.11.8 + akka-actor_2.11(2.4.2)> 后面两个是 maven 依赖。
<dependency><groupId>org.scala-lang</groupId><artifactId>scala-library</artifactId><version>2.11.8</version></dependency><dependency><groupId>com.typesafe.akka</groupId><artifactId>akka-actor_2.11</artifactId><version>2.4.2</version></dependency>
如果是 java7, 那么 jdk7 + <scala-library2.11.8 + akka-actor_2.11(2.3.14)>
<dependency><groupId>org.scala-lang</groupId><artifactId>scala-library</artifactId><version>2.11.8</version></dependency><dependency><groupId>com.typesafe.akka</groupId><artifactId>akka-actor_2.11</artifactId><version>2.3.14</version></dependency>
库的配置:
<repositories><repository><id>typesafe</id><name>Typesafe Repository</name><url>http://repo.typesafe.com/typesafe/releases/</url></repository> </repositories>
Scala-maven 插件配置:
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.8.1</version><configuration><includes><include>**/*.java</include><include>**/*.scala</include></includes></configuration></plugin><plugin><groupId>org.scala-tools</groupId><artifactId>maven-scala-plugin</artifactId><version>2.15.2</version><executions><execution><id>scala-compile-first</id><phase>process-resources</phase><goals><goal>compile</goal></goals></execution><execution><id>scala-test-compile</id><phase>process-test-resources</phase><goals><goal>testCompile</goal></goals></execution></executions></plugin>
转载于:https://www.cnblogs.com/lovesqcc/p/5297268.html
使用Scala实现Java项目的单词计数:串行及Actor版本相关推荐
- maven创建java项目_使用maven命令行创建java项目
一.先创建一个quick-start项目: 使用 mvn archetype:generate 进行创建,如果没有指定参数或者项目类型,交互系统首先会询问创建项目的基本类型, 如下所示,默认为类型7. ...
- 精通Java事务编程(8)-可串行化隔离级别之可串行化的快照隔离
本系列文章描述了DB并发控制的黯淡: 2PL虽保证了串行化,但性能和扩展不好 性能良好的弱隔离级别,但易出现各种竞争条件(丢失更新,写倾斜,幻读 串行化的隔离级别和高性能就是相互矛盾的吗?也许不是,一 ...
- java ado recordset_AdoDataSet.RecordSet的串行化和还原(转载)
标签: //数据集串行化 function RecordsetToXML(const Recordset: _Recordset): string; var RS: Variant; Stream: ...
- Java新手小白入门篇 Java项目的构建
前言 之前我们已经讲解了 JDK的下载安装及配置操作 和 IDEA的下载和安装 ,学习Java的准备工作我们已经做好了,下面我们需要做的就是创建项目,并且写我们的 Java 程序了 一.如何使用IDE ...
- Java中对象的串行化(Serialization)和transient关键字
Java中对象的串行化(Serialization)和transient关键字 前言: 该篇文章参考自网上资料,但是部分内容经过笔者更改,因此算作原创吧,原文地址: http://www.golden ...
- java input是关键字吗_Java中对象的串行化(Serialization)和transient关键字 - 金色阳光...
Java中对象的串行化(Serialization)和transient关键字: 对象的串行化(Serialization) 一.串行化的概念和目的 1.什么是串行化 对象的寿命通常随着生成该对象的程 ...
- Spark Core:Scala单词计数
Spark Core:Scala单词计数 文章目录 Spark Core:Scala单词计数 1.实验描述 2.实验环境 3.相关技能 4.知识点 5.实验效果 6.实验步骤 8.总结 1.实验描述 ...
- 软件工程小组项目——单词计数
软件工程小组项目--单词计数 项目地址 PSP表格 解题思路 实现过程 1.字符数 2.单词数 3.行数 4.各段行数统计 5.对子目录文件进行操作 6.图形界面 性能优化 实验总结 项目地址 Git ...
- 在Java项目中整合Scala
Scala是一个运行在Java JVM上的面向对象的语言.它支持函数编程,在语法上比Java更加灵活,同时通过Akka库,Scala支持强大的基于Actor的多线程编程.具有这些优势,使得我最近很想在 ...
最新文章
- 【云周刊】第146期:史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技...
- HUMAnN2:人类微生物组统一代谢网络分析2
- 如何在Ubuntu中安装java jdk
- 二叉苹果树(树型DP+背包)
- 好工作为什么会与你擦肩而过?
- [BZOJ1669][Usaco2006 Oct]Hungry Cows饥饿的奶牛
- java密码校验_java强密码校验
- codeforces 112APetya and Strings(字符串水题)
- 象棋人机对弈程序的思想
- 苹果自带的清理软件_苹果电脑系统垃圾清理软件
- 虚拟机安装的Ubuntu下载速度慢怎么办
- 计算机专业总人数所占比例公式,excel统计数据所占比例的教程详解
- Ubuntu16.04安装以及在 TitanX 下搭建 caffe框架(cuda8.0 + cudnn5.1)
- python 小括号 运算_浅析python 中大括号中括号小括号的区分
- 【MATLAB】求Taylor展开式
- es6根据对象属性获取到当前值的下标
- 机器学习笔记之配分函数(一)对数似然梯度
- 关于4A统一安全管理平台解决方案简单介绍
- PHP 经纬度坐标相关计算方法
- QT qmake makefile minGW