使用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288590.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vnc oracle solaris,在Solaris下安装VNC 远程安装Oracle

最近在折腾oracle ebs的安装 &#xff0c;用xmanager连接solairs10的过程中&#xff0c;总是连接3个小时左右就挂掉&#xff0c;很郁闷&#xff01;本文参考eygle的文章1&#xff1b;首先从 eygle提供的网址上下载vnc软件下载得到的文件如下 vnc-3.3.4-solaris_2.5.tar.tar然…

sql CHECK ,UNIQUE 约束(mysql)

check 用来限定值的范围&#xff0c;如下表&#xff1a; CREATE TABLE test22 ( age INT(10), sex VARCHAR(10), name11 VARCHAR(10) NOT NULL, CHECK (age>0) ) 在此&#xff0c;check限制了age的值为0以上 如果想让age的的不唯一呢&#xff1f;那就使用UNIQUE了&…

html+css常用小笔记(持续更新)

1去掉input点击时的蓝色边框outline:none; 2禁止文本选中-webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Chrome/Safari/Opera */ -khtml-user-select: none; /* Konqueror */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /…

音视频基本概念和FFmpeg的简单入门

写在前面最近正好有音视频编辑的需求&#xff0c;虽然之前粗略的了解过FFmpeg不过肯定是不够用的&#xff0c;借此重新学习下&#xff1b;基本概念容器/文件(Conainer/File)&#xff1a;即特定格式的多媒体文件&#xff0c;一般来说一个视频文件是由视频&#xff0c;音频&#…

linux shell之paste合并文件和找到匹配的文件里面替换内容(find和-exec或xargs组合)

1 问题 1&#xff09;合并2个文件&#xff0c;这里用paste命令 2&#xff09;找到匹配的文件里面替换内容&#xff0c;这里用find 和 -exec或xargs命令组合 2 实现 1&#xff09;合并2个文件&#xff0c;这里用paste命令&#xff0c;我们在paste后面加参数-d 然后加" &…

Windows Live Writer 的昨日荣光

今天这一篇文章&#xff0c;想写一写Windows Live Writer这款博客编辑器&#xff08;最早的一个版本是2007年发布的&#xff09;。毫不夸张地说&#xff0c;这是为数不多的几款所见即所得的编辑器之一&#xff0c;当然&#xff0c;它的运行速度慢也是一个众所周知的问题。作为一…

qpython执行手机脚本精灵使用教程_Android上执行python脚本-QPython

看书&#xff0c;发现android可以跑python。尝试了一下。首先需要在手机上安装python环境&#xff0c;通过安装apk实现&#xff0c;这个apk叫QPython&#xff0c;还有同类的比如SL4A。QPython的官网&#xff1a;https://www.qpython.com/可以在官网上下载QPython的安装包&#…

猎豹MFC--CMenu菜单 设置主菜单 给主对话框设置菜单 设置快捷菜单

设置主菜单&#xff08;不是快捷菜单&#xff09;&#xff1a;给主对话框设置菜单&#xff1a;效果如下&#xff1a;修改菜单的ID使之便于记忆&#xff1a;给菜单添加消息处理&#xff1a;添加处理代码&#xff1a;设置快捷菜单&#xff1a;打开对话框&#xff0c;属性添加消息…

SQL主键简单表述

主键&#xff08;PRIMARY KEY 约束&#xff09;&#xff1a;PRIMARY KEY 约束唯一标识数据库表中的每条记录。 主键就是唯一的&#xff0c;其是索引的一种&#xff0c;并且是唯一性索引的一种。 其实主键就像我们的身份证一样&#xff0c;每一个主键的id就表示着一个特定的一…

oracle 删除空间不足,oracle表空间扩容、创建、删除(解决表空间不足问题)

前言整理一下之前使用oracle数据库遇到的表空间不足的问题&#xff0c;顺便水个博客。oracle表空间操作语句1.改变已存在的数据文件的大小ALTER TABLESPACE app_data ADD DATAFILE ‘D:\ORACLE\PRODUCT\10.2.0\ORADATA\EDWTEST\APP03.DBF‘ SIZE 20480M;2.允许已存在的数据文件…

重复数据删除:块级技术VS.字节级技术

重复数据删除技术能够识别重复的数据&#xff0c;消除冗余&#xff0c;减少需转移或存储的数据的总体容量。在本文中&#xff0c;我将分别对这两种技术加以评论。与块级技术相比&#xff0c;字节级删除技术对数据的检查更加细微&#xff0c;精度更高&#xff0c;但同时需要更加…

Mac升级到Yosemite后默认的php版本不支持imagetfftext函数问题解决

Mac升级到yosemite后&#xff0c;php也自动升级&#xff0c;运行项目的时候发现后台验证码显示不出来。调试一下发现imagetfftext这个函数不存在&#xff0c;应该gd没有安装完全&#xff0c;因为Mac上的php实现系统自带的&#xff0c;只能通过重新安装php来解决【不能通过安装扩…

linux shell之字符串的更具字符分割和删除字符和文本内容的删除以及内容是否匹配成功

1 字符串的更具字符分割 1&#xff09; xargs分割 echo "chenyu*hello*word" | xargs -d "*" chenyu hello word 2&#xff09; awk分割 echo "chenyu*hello*word" | awk -F "*" {print $1} chenyu 2 字符串的删除字符 1&#xf…

将一个正方形分成4个大小一样的小正方形,再将其中一个小正方形分成4个小正方形,如此类推,分割n次是几个正方形?

问题&#xff1a; 将一个正方形分成4个大小一样的小正方形&#xff0c;再将其中一个小正方形分成4个小正方形&#xff0c;如此类推&#xff0c;分割n次是几个正方形&#xff1f; 解答&#xff1a; 一次分割都是将一个分成4个&#xff0c;增加了3&#xff08;1*3&#xff09;个…

领域事件和集成事件没那么高大上

前言随着系统架构的演变&#xff0c;有很多名词也随之涌现&#xff0c;如&#xff1a;微服务、灰度发布、资源隔离、容器、领域/集成事件等&#xff0c;听着的确高大上&#xff0c;让很多小伙伴有一种无法征服的感觉&#xff1b;其实很多东西可能之前就已经用过了&#xff0c;只…

python截图拼接_Python实现屏幕截图有两种方式 - 小众知识

使用windows API使用PIL中的ImageGrab模块下面对两者的特点和用法进行详细解释。一、Python调用windows API实现屏幕截图好处是灵活速度快缺点是&#xff1a;写法繁琐不跨平台import timeimport win32gui, win32ui, win32con, win32apidef window_capture(filename):hwnd 0 #…

sql drop 简单操作(Mysql)

通过使用 DROP 语句&#xff0c;可以轻松地删除索引、表和数据库。 例如&#xff0c;删除一个表&#xff0c;表名为 table1 的表&#xff1a; drop table <表名>; drop table table1 ; drop删除数据库 test1: drop database <数据库名>; drop database test1…

20142335郝昊第三周学习总结

20145335郝昊 《Java程序设计》第3周学习总结 教材学习内容总结 第四章 类与对象 定义&#xff1a; 对象&#xff08;Object&#xff09;&#xff1a;存在的具体实体&#xff0c;具有明确的状态和行为。 类&#xff08;Class&#xff09;:具有相同属性和行为的一组对象的集合&…

Python面向对象编程基础

#codingutf-8class hello:def __init__(self,NAME):self.nameNAMEdef sayhello(self):print("Hello, {0}".format(self.name)) #拼接字符串&#xff0c;也可以用print("Hello "self.name)#类的继承 class hi(hello): #父类用括号括起来def sayhi(self):pr…

书生云王东临:真正的超融合产品要像“机器猫” 开箱即用

“经过6年的发展&#xff0c;我们已经为技术找到了非常好的落地产品&#xff0c;可以突出我们的优势&#xff0c;现在到了我们大展宏图的时候了。”20年前书生集团从电子公文业务起家&#xff0c;后来经营过电子书网站&#xff0c; 6年前开始进入云存储领域&#xff0c;在存储技…