其实我想找一门“具有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.basic

import 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 = " -!\"#$%&()*,./:;?@[]^_`{|}~+<=>\\".toArray launch() 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.toList
sortByValue(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).listFiles
if (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.Source
val 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.concurrent

import java.lang.Thread

import akka.actor.{ActorRef, Props, ActorSystem, Actor}
import akka.actor.Actor.Receive import scala.collection.immutable.List
import scala.collection.mutable
import scala.collection.mutable.{ArrayBuffer, Map, HashMap}
import java.io.File import scalastudy.basic.WordStat /**
* Created by lovesqcc on 16-3-19.
*/
object ConcurrentWordStat extends App { val seps = " -!\"#$%&()*,./:;?@[]^_`{|}~+<=>\\".toArray launch() 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 ! path Thread.sleep(6000) val concurrentResult:Map[String,Int] = sortByValue(StatWordActor.finalResult()) WordStat.init()
val allWords = WordStat.fetchAllJavaFiles(path)
.map(WordStat.readFile(_))
.map(WordStat.analysisWords(_)).flatten.toList
val basicResult:Map[String,Int] = sortByValue(WordStat.statWords(allWords)) // Compare the results of serial version and actors version
concurrentResult.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).listFiles
if (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 = 0
private 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.Source
val 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() = stat private var recvCount = 0
private 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>

使用Scala实现Java项目的单词计数:串行及Actor版本的更多相关文章

  1. Scala IDEA for Eclipse里用maven来创建scala和java项目代码环境(图文详解)

    这篇博客 是在Scala IDEA for Eclipse里手动创建scala代码编写环境. Scala IDE for Eclipse的下载.安装和WordCount的初步使用(本地模式和集群模式) ...

  2. 用maven来创建scala和java项目代码环境(图文详解)(Intellij IDEA(Ultimate版本)、Intellij IDEA(Community版本)和Scala IDEA for Eclipse皆适用)(博主推荐)

    不多说,直接上干货! 为什么要写这篇博客? 首先,对于spark项目,强烈建议搭建,用Intellij IDEA(Ultimate版本),如果你还有另所爱好尝试Scala IDEA for Eclip ...

  3. Scala:Java 项目中混入scala代码

    Spark 是用Scala代码写的.为了调试Spark,做了如下尝试. 1.Eclipse下:Java 项目 ,Using Maven,编写了一个java 版Spark应用. Spark的代码(sca ...

  4. Spark:用Scala和Java实现WordCount

    http://www.cnblogs.com/byrhuangqiang/p/4017725.html 为了在IDEA中编写scala,今天安装配置学习了IDEA集成开发环境.IDEA确实很优秀,学会 ...

  5. java面试一日一题:再谈垃圾回收器中的串行、并行、并发

    问题:请讲下java中垃圾回收器的串行.并行.并发 分析:该问题主要考察在垃圾回收过程中垃圾回收线程和用户线程的关系 回答要点: 主要从以下几点去考虑, 1.串行.并行.并发的概念 2.如何考虑串行. ...

  6. Spark: 单词计数(Word Count)的MapReduce实现(Java/Python)

    1 导引 我们在博客<Hadoop: 单词计数(Word Count)的MapReduce实现 >中学习了如何用Hadoop-MapReduce实现单词计数,现在我们来看如何用Spark来 ...

  7. 在Java项目中整合Scala

    Scala是一个运行在Java JVM上的面向对象的语言.它支持函数编程,在语法上比Java更加灵活,同时通过Akka库,Scala支持强大的基于Actor的多线程编程.具有这些优势,使得我最近很想在 ...

  8. [大数据从入门到放弃系列教程]在IDEA的Java项目里,配置并加入Scala,写出并运行scala的hello world

    [大数据从入门到放弃系列教程]在IDEA的Java项目里,配置并加入Scala,写出并运行scala的hello world 原文链接:http://www.cnblogs.com/blog5277/ ...

  9. Maven Java项目添加Scala语言支持

    为了在一个普通的使用Maven构建的Java项目中,增加对Scala语言的支持.使得其能够同时编译Java和Scala语言的文件.其实很简单的一件事情,只需要在pom.xml文件中的build部分中的 ...

随机推荐

  1. sql to_char 日期转换字符串

    1.转换函数 与date操作关系最大的就是两个转换函数:to_date(),to_char() to_date() 作用将字符类型按一定格式转化为日期类型: 具体用法:to_date('2004-11 ...

  2. struts2常用标签使用说明

    在struts2中,用的是s标记,先在jsp文件中引入标记:<%@ taglib prefix="s" uri="/struts-tags"%> & ...

  3. AJAX 后台返回多种数据

    前台ajax(jsp文件): (1) $.ajax({ type: "POST", url: "/dragable/demo/finishTopo", asyn ...

  4. # 20145334赵文豪 《Java程序设计》第7周学习总结

    20145334赵文豪 <Java程序设计>第7周学习总结 教材学习内容总结 第十三章 时间与日期 13.1.1时间的度量 1.格林威治时间(GMT):参考太阳到达最高点,有时间误差. 2 ...

  5. 邮件格式(HTML/TXT),TXT为文本邮件

    <?phpclass smtp{/* Public Variables */var $smtp_port;var $time_out;var $host_name;var $log_file;v ...

  6. 一些常用的NLTK频率分布类中定义的函数

    fdist=FreqDist(samples)创建包含给定样本的频率分布fist.inc(sample)增加样本fdist['monstrous']计数给定样本出现的次数fdist.freq('mon ...

  7. 【转】NumPy-快速处理数据

    2.0 简介 标准安装的Python中用列表(list)保存一组值,可以用来当作数组使用,不过由于列表的元素可以是任何对象,因此列表中所保存的是对象的指针(为了保存各种类型的对象,只能牺牲空间).这样 ...

  8. Total Commander 常用快捷键(并附快捷键大全)

    Total Commander 常用快捷键 喜欢用Total Commander的人,都会记住它的一些快捷键,这会给你的操作带来很大的方便,以下是经常会用到的快捷键,大家可以记住一些自己用得最多的操作 ...

  9. 【转】Spring 获取web根目录 (Spring线程获取web目录/路径/根目录,普通类获取web目录)

    不使用Spring,怎样能在Listener启动的Thread中获取web目录,还真不完全确定.其实我觉得实际代码也很简单.就是基于普通的listener,然后在listener中获取web目录并放到 ...

  10. JAVA中I/O流

    IO流分为输入流(InputStream)和输出流(OutputStream)两类 按流所处理的数据类型又可以分为字节流和字符流(用于处理Unicode字符数据)两类 字节流主要是由 InputStr ...