其实我想找一门“具有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. [MetroUI-1]无边框模式

    Wpf中取消边框,使用 WindowStyle="None" AllowsTransparency="True"

  2. [LintCode] Paint Fence 粉刷篱笆

    There is a fence with n posts, each post can be painted with one of the k colors.You have to paint a ...

  3. java web(四)文件上传与下载

     一.文件上传原理 1.在TCP/IP中,最早出现的文件上传机制是FTP ,它是将文件由客户端发送到服务器的标准机制:但是在jsp使用过程中不能使用FTP方法上传文件,这是由jsp运行机制所决定. 通 ...

  4. E: dpkg 被中断,您必须手工运行 sudo dpkg --configure -a 解决此问题。

    学习 : http://blog.csdn.net/darennet/article/details/9009361 http://www.uedsc.com/dpkg-sudo-dpkg-confi ...

  5. javascript特效--制作背景电子钟(整点时祝贺生日快乐)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 演示一个OLS进行数据访问控制的示例

    1.确认数据库版本 2.安装OLS组件 3.创建策略 4.创建分级和标签 5.创建测试用户并授权 6.更新标签 7.测试演示

  7. VMwareTools安装笔记

    1.首先确保你的虚拟机正在运行. 2.右键点击“安装VMwareTools”,如果安装过了,会显示“重新安装...” 3.系统会自动打开VMwareTools的目录,(一般都会自动打开,因为系统一般会 ...

  8. shell 常用正则

    shell常用正则表达式   “^\d+$” //非负整数(正整数 + 0)   “^[0-9]*[1-9][0-9]*$” //正整数   “^((-\d+)|(0+))$” //非正整数(负整数 ...

  9. java中的动态代理

    1.动态代理的定义:为其他对象提供一个代理以控制对这个对象的访问 2.通过api看下proxy生成代理类的2中写法: 创建某一接口 Foo 的代理: InvocationHandler handler ...

  10. Cocos2d-x环境搭建

    资源列表 官网上下载最新的cocos2d-x-3.3. 安装JDK,Eclipse,CDT插件,ADT插件. 下载Android SDK,更新.因为国内经常访问不了google,用vpn速度也有点慢. ...