scala官方网址:html
Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各类特性。Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序。http://www.scala-lang.orgjava
一、优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。react
二、速度快:Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,因此和JRuby,Groovy比起来速度会快不少。程序员
三、能融合到Hadoop生态圈:Hadoop如今是大数据事实标准,Spark并非要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java作出来的API太丑,或者想实现一个优雅的API太费劲。 es6
由于Scala是运行在JVM平台上的,因此安装Scala以前要安装JDK。正则表达式
访问Scala官网http://www.scala-lang.org/下载Scala编译器安装包,目前最新版本是2.12.x,这里下载scala-2.11.8.msi后点击下一步就能够了(自动配置上环境变量)。也能够下载scala-2.11.8.zip,解压后配置上环境变量就能够了。算法
下载Scala地址https://www.scala-lang.org/download/2.11.8.htmlapache
而后解压Scala到指定目录编程
tar -zxvf scala-2.11.8.tgz -C /usr/java
配置环境变量,将scala加入到PATH中
vi /etc/profile export JAVA_HOME=/usr/java/jdk1.8 export PATH=$PATH:$JAVA_HOME/bin:/usr/java/scala-2.11.8/bin |
目前Scala的开发工具主要有两种:Eclipse和IDEA,这两个开发工具都有相应的Scala插件,若是使用Eclipse,直接到Scala官网下载便可http://scala-ide.org/download/sdk.html。
因为IDEA的Scala插件更优秀,大多数Scala程序员都选择IDEA,能够到http://www.jetbrains.com/idea/download/下载,点击下一步安装便可,安装时若是有网络能够选择在线安装Scala插件。
这里咱们使用离线安装Scala插件:
1.安装IDEA,点击下一步便可。
2.下载IEDA的scala插件
插件地址: https://plugins.jetbrains.com/plugin/1347-scala
3.安装Scala插件:Configure -> Plugins -> Install plugin from disk -> 选择Scala插件 -> OK -> 重启IDEA
REPL ==> 交互式解析器环境
R(read)、E(evaluate) 、P(print)、L(loop)
输入值,交互式解析器会读取输入内容并对它求值,再返回结果,并重复此过程。(所见即所得)
REPL特性:
变量在会话周期内一直可用
多行代码和单行代码一块儿编译
支持连接外部库和代码
REPL历史命令跨会话存储
在命令行输入scala以启动scala REPL
File ==> New ==> Project
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
<!-- 若是想要用java -jar 来运行咱们打包以后的jar包,则下面这个配置必须注释掉 -->
<!-- <scope>provided</scope>-->
</dependency>
</dependencies>
<build>
<plugins>
<!-- 限制jdk版本插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 编译scala须要用到的插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 项目打包用到的插件 -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.itcast.scala.demo1.ScalaFirst</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
src ==> main ==> new ==> Directory ==> scala
开发代码以下:
object ScalaFirst {
def main(args: Array[String]): Unit = {
println("hello world")
}
}
将咱们的代码打包,以后,进行运行
双击package以后,就会出现咱们打好的jar包,而后选择下面这个就能够运行了
运行咱们的代码一共能够有四个命令,两种是打包的时候选择了咱们的main程序类的,两种是咱们打包时候没有选择main程序类的
其中第一种和第三种,都是选定了咱们的main程序类
第二种和第四种都是没有选定咱们的main程序类
四种运行方式均可以用于运行咱们的jar包
第一种运行方式:咱们打包的时候指定了对应的main方法所在的程序类
scala scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第二种运行方式:无论打包的时候有没有指定main方法所在的程序类,均可以运行
scala -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
第三种方式:咱们打包的时候指定了对应的main方法所在的程序类
java -jar scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第四种方式:无论打包的时候有没有指定main方法所在的程序类,均可以运行
java -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
咱们能够看到咱们的scala代码最终也编译成了class文件
scala当中的变量申明可使用两种方式,第一种使用val来申明变量。第二种使用var来申明变量。
申明变量语法
val/var 变量名 [:变量类型] = 变量值
其中val定义的变量是不可变的。相似于java当中使用final来进行修饰
REPL自动分配变量名
若是咱们在REPL当中没有定义变量名,那么咱们的变量名系统会自动给定
注意:scala当中的变量类型能够不用指定,系统会自动推断。为了减小可变性引发的bug,scala当中推荐尽可能使用不可变类型来申明变量。var和val申明变量的时候,变量都必须初始化
定义变量时用 {} 包含一系列表达式,其中块的最后一个表达式的值就是块的值。
var hello = {
println("world")
val d = 20
val c = 10
d-c
}
块表达式最后的结果,就是咱们变量的值
Scala和Java同样,有7种数值类型Byte、Char、Short、Int、Long、Float、Double类型和1个Boolean类型。
Boolean |
true 或者 false |
Byte |
8位, 有符号 |
Short |
16位, 有符号 |
Int |
32位, 有符号 |
Long |
64位, 有符号 |
Char |
16位, 无符号 |
Float |
32位, 单精度浮点数 |
Double |
64位, 双精度浮点数 |
String |
其实就是由Char数组组成 |
与java当中不一样,scala当中并不区分基本数据类型和引用数据类型,全部的这些类型所有都是对象,能够调用相对应的方法。在scala当中,String直接引用的是java.lang.String这个java当中的类型。因为String在须要时能隐式转换为StringOps,所以不须要任何额外的转换,String就可使用这些方法。
每一种数据类型都有对应的Rich* 类型,如RichInt、RichChar等,为基本类型提供了更多的有用操做。
Scala中,全部的值都是类对象,而全部的类,包括值类型,都最终继承自一个统一的根类型Any。统一类型,是Scala的又一大特色。更特别的是,Scala中还定义了几个底层类(Bottom Class),好比Null和Nothing。
1) Null是全部引用类型的子类型,而Nothing是全部类型的子类型。Null类只有一个实例对象,null,相似于Java中的null引用。null能够赋值给任意引用类型,可是不能赋值给值类型。
2) Nothing,能够做为没有正常返回值的方法的返回类型,很是直观的告诉你这个方法不会正常返回,并且因为Nothing是其余任意类型的子类,他还能跟要求返回值的方法兼容。
3) Unit类型用来标识过程,也就是没有明确返回值的函数。 因而可知,Unit相似于Java里的void。Unit只有一个实例,(),这个实例也没有实质的意义。
+-*/%能够完成和Java中相同的工做,可是有一点区别,他们都是方法。你几乎能够用任何符号来为方法命名。
举例:
scala> 1 + 2 等同于: scala> 1.+(2) |
注意:Scala中没有++、--操做符,须要经过+=、-=来实现一样的效果。
scala中没有三目运算符,由于根本不须要。scala中if else表达式是有返回值的,若是if或者else返回的类型不同,就返回Any类型(全部类型的公共超类型)。
例如:if else返回类型同样
val a = 20
val b = if(a >10){
15
}else{
35
}
例如:if else返回类型不同
val c = 50
val d = if(c > 20){
println("返回一个字符串")
"ABC"
}else{
println("helloworld")
}
若是缺乏一个判断,什么都没有返回,可是Scala认为任何表达式都会有值,对于空值,使用Unit类,写作(),叫作无用占位符,至关于java中的void。
注意:行尾的位置不须要分号,只要可以从上下文判断出语句的终止便可。可是若是在单行中写多个语句,则须要分号分割。在Scala中,{}快包含一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。
scala提供了相似于java的while和do循环,可是while语句的自己是没有任何返回值类型的,也就是while语句最终的返回结果是Unit类型的()。
var e = 1;
val f = while(e <= 10){
e +=1
}
println(e)
println(f)
scala当中while循环的contine和break:注意:scala当中并无提供相似于java的continue和break操做,若是须要终止循环,咱们能够有如下几种方式
一、 使用Boolean标识来进行终端
二、 使用嵌套函数,从函数中直接return
三、 使用Breaks对象当中的break方法
var g = 10
val loop = new Breaks
loop.breakable{
val h = while(g <=20){
g +=1
if(g == 15){
loop.break()
}
}
println(h)
}
println(g+"=============")
scala当中,为for循环这一经常使用的循环结构提供了不少的特性,这些特性被称之为for推导式或者for表达式
示例一:使用to实现左右两边均为闭合的访问
for(i <- 1 to 3; j <- 1 to 5){
println( i *j +" result result result result")
}
示例二:使用util实现左右两边分别为前闭后开的访问
for(i <- 1 until 5 ;j <- 2 until 5){
println(i * j )
}
示例三:引入保护式(也称条件判断式)。咱们能够在for循环条件里面加入判断表达式,若是知足则进入for循环,若是不知足则不进入for循环。相似于java当中的continue的功能相似
for(i <- 1 to 5 if i!=2){
println(i)
}
示例四:引入变量
for(i <- 1 to 3 ;j = 4-i){
println(i * j)
}
示例五:将遍历过程当中处理的结果返回到一个变量,使用yield关键字进行接收
val for5 = for(i <- 1 to 10) yield i
println(for5+"for5")
示例六:使用大括号代替小括号
for{
i <- 1 to 5
j = 5-i
}
println( i* j +"myij")
在scala中,通常状况下咱们不会刻意的去区分函数与方法的区别,可是他们确实是不一样的东西。后面咱们再详细探讨。首先咱们要学会使用scala来调用函数与方法。
1) 调用函数,求方根
scala> import scala.math._ scala> sqrt(100) |
2) 调用方法,静态方法(scala中没有静态方法这个概念,须要经过伴生类对象来实现)
生成一个随机的素数
scala> BigInt.probablePrime(16, scala.util.Random) |
3) 调用方法,非静态方法,使用对象调用
scala> "HelloWorld".distinct |
4) apply与update方法
apply方法是调用时能够省略方法名的方法。用于构造和获取元素:
"Hello"(4) 等同于 "Hello".apply(4) Array(1,2,3) 等同于 Array.apply(1,2,3) 如: println("Hello"(4)) println("Hello".apply(4)) |
在StringOps中你会发现一个 def apply(n: Int): Char方法定义。update方法也是调用时能够省略方法名的方法,用于元素的更新:
arr(4) = 5 等同于 arr.update(4,5) 如: val arr1 = new Array[Int](5) arr1(1) = 2 arr1.update(1, 2) println(arr1.mkString(",")) |
在scala当中,函数与方法是两个不一样的概念,函数是scala当中的一等公民,scala是一门函数式的编程语言,同时兼顾了面向对象语言的特性
scala定义方法的标准格式为
def 方法名(参数名1: 参数类型1, 参数名2: 参数类型2) : 返回类型 = {方法体} |
示例一:定义一个最标准的方法,且定义方法的返回值类型为Int类型
def hello(first:String,second:Int) :Int = {
second
}
示例二:定义一个方法,且不定义返回值
注意:若是定义的方法没有返回值,那么方法的返回值会作自动推断。根据咱们方法的最后一个返回类型来推断咱们的方法返回类型
def hello2(first:Int , second:String) ={
//println(first)
//20
}
val hello2Result = hello2(20,"abc")
println( hello2Result)
示例三:定义一个方法,不定义返回值,能够经过自动推断,返回不一样类型的值
def hello3(first:Int,second:String) ={
if(first > 10){
first
}else{
second
}
}
val hello3Result = hello3(5,"helloworld")
println(hello3Result)
示例四:定义一个方法,参数给定默认值,若是不传入参数,就使用默认值来代替
def hello4(first:Int = 10,second:String)={
println(first+"\t"+ second)
}
//注意咱们在调用方法的时候咱们能够经过参数名来指定咱们的参数的值
hello4(second="helloworld")
示例五:变长参数,方法的参数个数不定的,相似于java当中的方法的...可变参数
def hello5(first:Int*)={
var result = 0;
for(arg <- first){
result += arg
}
println(result)
}
hello5(10,20,30)
hello5(10,50)
示例六:递归函数。咱们能够定义一个方法,使得方法本身调用本身,造成一个递归函数,可是方法的返回值类型必须显示的手动指定
def hello6(first:Int):Int={
if(first <= 1){
1
}else{
first * hello6(first -1)
}
}
val hello6Result = hello6(10)
println(hello6Result)
示例七:定义一个方法,没有显示的指定返回值,那么咱们方法当中定义的等号能够省掉
注意:若是省掉了=号,那么这个方法强调的就是一个代码执行的过程
/**
* 定义了一个方法,可是方法的返回值没有显示指定,
* 此时咱们就能够省掉方法定义的=号,若是省掉 = 号,
* 那么这个方法强调的是一个过程,代码执行的过程,
* 不会产生任何的返回值
* @param first
*/
def hello7(first:Int){
println(first)
30
}
hello7(20)
示例八:直接经过def定义一个方法
def hello8=10;
val hello8Result = hello8
println(hello8Result)
示例九:若是方法体当中只有一行代码,咱们也能够省掉大括号
def hello10(first:Int,second:Int) = first+second
val hello10Result = hello10(10,20)
println(hello10Result)
函数定义的两种形式
第一种形式:
val 函数名 = (参数名1:参数类型1,参数名2:参数类型2) => {函数体}
第二种形式:
val 函数名 :(参数类型1,参数类型2) => (返回类型) = {
函数体
}
示例一:定义一个标准函数,使用 =>来进行定义
val func1 =(x:Int,y:Int) =>{
x+y
}
func1(2,8)
示例二:定义匿名函数。也就是咱们能够定义一个没有名字的函数
定义一个匿名函数以后,这个函数就无法使用了
(x:Int,y:String) =>{x + y}
示例三:函数定义的另一种形式,定义一个函数,参数只有一个且是Int类型,返回值也是Int类型
val func3 :Int => Int = {x => x * x }
val func3Result = func3(10)
示例四:定义一个函数,参数值是两个,分别是Int和String,返回值是一个元组,分别是String和Int
val func4:(Int,String) =>(String,Int) ={
(x,y) => (y,x)
}
val func4Result = func4(10,"hello")
println(func4Result)
在scala当中,函数与方法是有区别的,函数能够做为一个参数,传入到方法里面去
咱们能够定义一个函数,而后再定义一个方法,可是方法的参数是一个函数
val myFunc = (x:Int) =>{
x * x
}
val myFunc2 :(Int) => Int ={
x => x * x
}
def methodFunction(f:Int => Int):Int ={
println(f(100))
f(100)
}
val methodFunctionResult = methodFunction(myFunc)
val methodFunctionResult2 = methodFunction(myFunc2)
println(methodFunctionResult)
println(methodFunctionResult2)
方法能够自动转换成函数做为参数传递到方法里面去
def method2(x:Int) ={ x * x }
def methodFunc2(x:Int => Int):Int ={
x(100)
}
val methodFunc2Result = methodFunc2(method2)
println(methodFunc2Result)
咱们能够经过 _ 将咱们的一个方法,转换成函数
def method3(x:Int,y:String ) :Int = {
println(x)
x
}
val methodToFunc = method3 _
println( methodToFunc)
当val被声明为lazy时,他的初始化将被推迟,直到咱们首次对此取值,适用于初始化开销较大的场景。
def init(): String = {
println("init方法执行")
"嘿嘿嘿,喵喵喵~"
}
lazy val msg = init()
println("lazy方法没有执行")
println(msg)
Scala同时支持可变集合和不可变集合,不可变集合从不可变,能够安全的并发访问。
两个主要的包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
Scala优先采用不可变集合,对于几乎全部的集合类,Scala都同时提供了可变和不可变的版本。
不可变集合继承层次:
可变集合继承层次:
咱们能够定义一个固定长度大小和类型的定长数组
//定义一个数组长度为10,类型为Int的固定大小数组
val array = new Array[Int](10)
array(1) = 10
array(2) = 20
//访问数组当中的某一个下标元素值
println(array(1))
//直接使用apply方法进行生成一个数组
val array2 = Array(1,2,3)
//访问数组的元素
println(array2(2))
咱们也能够经过ArrayBuffer来定义一个变长数组
val array3 = new ArrayBuffer[Int]()
array3.append(20)
val array4 = ArrayBuffer[String]()
array4.append("helloworld")
定长数组转换成变长数组
//定长数组转换成变长数组
val toBuffer = array.toBuffer
toBuffer.append(50)
变长数组转换为定长数组
//变长数组准换成定长数组
val toArray = array3.toArray
咱们能够经过Array的ofDim方法来定义一个多维的数组,多少行,多少列,都是咱们本身定义说了算
val dim = Array.ofDim[Double](3,4)
dim(1)(1) = 11.11
println(dim.mkString(","))
val array5 = ArrayBuffer(1,2,3,4,5,6)
for(x <- array5){
println(x )
}
val array6 = Array(1,2,3,4,5,6)
//求和
array6.sum
//求最大值
array6.max
//排序
array6.sorted
在scala当中提供元组tuple的数据类型,能够理解tuple为一个容器,能够存放各类不一样的数据类型的数据,例如一个Tuple当中既能够存放String类型数据,同时也能够存放Int类型的数据
注意:注意元组一旦建立以后,就是不可变的,也就是说元组当中没有添加和删除元素这一说
建立元组,直接使用小括号,小括号当中存放咱们元组当中各类类型的元素便可
val tuple1 = ("hello",1,5.0f)
println(tuple1)
访问元组当中的数据直接使用_加角标便可,可是要注意,元组当中的数据角标是从1开始的
val tuple1 = ("hello",1,5.0f)
println(tuple1)
val tuple1Result = tuple1._1
println(tuple1Result)
val tuple1 = ("hello",1,5.0f)
println(tuple1)
val tuple1Result = tuple1._1
println(tuple1Result)
//第一种方式遍历元组
for(x <- tuple1.productIterator){
println(x)
}
//第二种方式遍历元组
tuple1.productIterator.foreach( x => println(x))
scala当中的Map集合与java当中的Map相似,也是key,value对形式的
val map1 = Map("hello" ->"world","name" -> "zhangsan","age" -> 18)
val map2 = scala.collection.mutable.Map("hello" ->"world","name" -> "zhangsan","age" -> 18)
//可变map添加元素
map2.+=("address" ->"地球")
println(map2)
//可变map删除元素.注意,删除元素是返回一个删除元素以后的map,原来的map并无改变
val map3 = map2.-("address")
println(map2)
println(map3)
//或者使用覆盖key的方式来更细元素
map2 += ("address" -> "北京")
println(map2)
//或者使用 + 来进行更新元素
//注意,map当中没有phonNo这个key,则不能更细
map2 +("address" ->"上海","phonNo" -> "13688886666")
println(map2)
//经过key来进行取值
map2.get("address")
//经过key来进行取值,若是没有这个key,就用后面给定的默认值
map2.getOrElse("address","非洲")
//经过key来进行取值,真的没有这个key,那么就用后面给定的默认值
map2.getOrElse("phoNo","13133335555")
//遍历key与value
for((k,v) <- map2){
println(k)
println(v)
}
//遍历获取全部的key
for(k <- map2.keys) {
println(k)
}
//遍历获取全部的value
for(v <- map2.values) {
println(v)
}
//打印key,value对
for(kv <- map2){
println(kv)
}
//将对偶的元组转变为map
val arrayMap = Array(("name","zhangsan"),("age",28))
val toMap = arrayMap.toMap
println(toMap)
scala当中也提供有与java相似的List集合操做
注意:列表当中的元素类型能够是不一样的,这一点与咱们元组相似,可是列表当中的元素是能够删减的
val list1 = List("hello",20,5.0f)
println(list1)
//访问列表当中的元素
val list1Result = list1(0)
println(list1Result)
咱们能够从列表头部或者尾部添加元素
val list2 = list1:+50
val list3 = 100+:list1
println(list2)
println(list3)
Nil是一个空的List,定义为List[Nothing]
//尾部添加了Nil,那么就会出现List集合里面装List集合的现象
val list4 = 1::2 ::3 :: list1 ::Nil
println(list4)
//尾部没有添加Nil的值,那么全部的元素都压平到一个集合里面去了
val list5 = 1::2::3::list1
println(list5)
val list6 = new ListBuffer[String]
list6.append("hello")
list6.append("world")
println(list6.mkString(","))
val list7 = list6.toList
println(list7)
Scala是函数式风格与面向对象共存的编程语言,方法不该该有反作用是函数风格编程的一个重要的理念。方法惟一的效果应该是计算并返回值,用这种方式工做的好处就是方法之间不多纠缠在一块儿,所以就更加可靠和可重用。另外一个好处(静态类型语言)是传入传出方法的全部东西都被类型检查器检查,所以逻辑错误会更有可能把本身表现为类型错误。把这个函数式编程的哲学应用到对象世界里觉得着使对象不可变。
前面一章介绍的Array数组是一个全部对象都共享相同类型的可变序列。比方说Array[String]仅包含String。尽管实例化以后你没法改变Array的长度。所以,Array是可变的对象。
说到共享相同类型的不可变对象类型,Scala的List类才是。和数组同样,List[String]包含的仅仅是String。Scala的List不一样于Java的java.util.List,老是不可变的(Java的List是可变)。更准确的说法,Scala的List是设计给函数式风格的编程用的。
(1)List类型定义以及List的特色:
//字符串类型List
scala> val fruit=
List(
"Apple",
"Banana",
"Orange")
fruit:
List[String] =
List(Apple, Banana, Orange)
//前一个语句与下面语句等同
scala> val fruit=
List.apply(
"Apple",
"Banana",
"Orange")
fruit:
List[String] =
List(Apple, Banana, Orange)
//数值类型List
scala> val nums=
List(
1,
2,
3,
4,
5)
nums:
List[Int] =
List(
1,
2,
3,
4,
5)
//多重List,List的子元素为List
scala> val
list =
List(
List(
1,
2,
3),
List(
"adfa",
"asdfa",
"asdf"))
list:
List[
List[Any]] =
List(
List(
1,
2,
3),
List(adfa, asdfa, asdf))
//遍历List
scala>
for(i <-
list; from=i; j<-from)println(j)
1
2
3
adfa
asdfa
asdf
(2)List与Array的区别:
一、List一旦建立,已有元素的值不能改变,可使用添加元素或删除元素生成一个新的集合返回。
如前面的nums,改变其值的话,编译器就会报错。而Array就能够成功
scala>nums(
3)=
4
<
console>:
10: error: value update
is
not a member
of List[Int]
nums(
3)=
4
^
二、List具备递归结构(Recursive Structure),例如链表结构
List类型和睦他类型集合同样,它具备协变性(Covariant),即对于类型S和T,若是S是T的子类型,则List[S]也是List[T]的子类型。
例如:
scala>
var listStr:
List[
Object] =
List(
"This",
"Is",
"Covariant",
"Example")
listStr:
List[
Object] =
List(This, Is, Covariant, Example)
//空的List,其类行为Nothing,Nothing在Scala的继承层次中的最底层
//,即Nothing是任何Scala其它类型如String,Object等的子类
scala>
var listStr =
List()
listStr:
List[Nothing] =
List()
scala>
var listStr:
List[
String] =
List()
listStr:
List[
String] =
List()
(3)List经常使用构造方法
//一、经常使用::及Nil进行列表构建
scala> val nums =
1 :: (
2:: (
3:: (
4 :: Nil)))
nums:
List[Int] =
List(
1,
2,
3,
4)
//因为::操做符的优先级是从右向左的,所以上一条语句等同于下面这条语句
scala> val nums =
1::
2::
3::
4::Nil
nums:
List[Int] =
List(
1,
2,
3,
4)
至于
::
操做符的使用将在下面介绍
(4)List经常使用操做
//判断是否为空
scala> nums.isEmpty
res5: Boolean =
false
//取第一个元素
scala> nums.head
res6: Int =
1
//取列表第二个元素
scala>nums.tail.head
res7: Int =
2
//取第三个元素
scala>nums.tail.tail.head
res8: Int =
3
//插入操做
//在第二个位置插入一个元素
scala>nums.head::(
3::nums.tail)
res11: List[Int] = List(
1,
3,
2,
3,
4)
scala> nums.head::(nums.tail.head::(
4::nums.tail.tail))
res12: List[Int] = List(
1,
2,
4,
3,
4)
//插入排序算法实现
def isort(xs: List[Int]):List[Int] = {
if(xs.isEmpty)
Nil
else insert(xs.head, issort(xs.tail))
}
def insert(x:Int, xs:List[Int]):List[Int] = {
if(xs.isEmpty || x <= xs.head) x::xs
else xs.head :: insert(x, xs.tail)
}
//链接操做
scala>List(
1,
2,
3):::List(
4,
5,
6)
res13: List[Int] = List(
1,
2,
3,
4,
5,
6)
//去除最后一个元素外的元素,返回的是列表
scala> nums.init
res13: List[Int] = List(
1,
2,
3)
//取出列表最后一个元素
scala>nums.last
res14: Int =
4
//列表元素倒置
scala> nums.reverse
res15: List[Int] = List(
4,
3,
2,
1)
//一些好玩的方法调用
scala> nums.reverse.reverse == nums
//丢弃前面n个元素
scala>nums drop
3
res16: List[Int] = List(
4)
//获取前面n个元素
scala>nums take
1
res17: List[Int] = List[
1]
//将列表进行分割
scala> nums.splitAt(
2)
res18: (List[Int], List[Int]) = (List(
1,
2),List(
3,
4))
//前一个操做与下列语句等同
scala> (nums.take(
2),nums.drop(
2))
res19: (List[Int], List[Int]) = (List(
1,
2),List(
3,
4))
//Zip操做
scala> val nums=List(
1,
2,
3,
4)
nums: List[Int] = List(
1,
2,
3,
4)
scala> val chars=List(
'1',
'2',
'3',
'4')
chars: List[Char] = List(
1,
2,
3,
4)
//返回的是List类型的元组(Tuple),返回的元素个数与最小的List集合的元素个数同样
scala> nums zip chars
res20: List[(Int, Char)] = List((
1,
1), (
2,
2), (
3,
3), (
4,
4))
//List toString方法
scala> nums.toString
res21: String = List(
1,
2,
3,
4)
//List mkString方法
scala> nums.mkString
res22: String =
1234
//转换成数组
scala> nums.toArray
res23: Array[Int] = Array(
1,
2,
3,
4)
(5)List伴生对象方法
//apply方法
scala>
List.apply(
1,
2,
3)
res24:
List[Int] =
List(
1,
2,
3)
//range方法,构建某一值范围内的List
scala>
List.range(
2,
6)
res25:
List[Int] =
List(
2,
3,
4,
5)
//步长为2
scala>
List.range(
2,
6,
2)
res26:
List[Int] =
List(
2,
4)
//步长为-1
scala>
List.range(
2,
6,
-1)
res27:
List[Int] =
List()
scala>
List.range(
6,
2 ,
-1)
res28:
List[Int] =
List(
6,
5,
4,
3)
//构建相同元素的List
scala>
List.make(
5,
"hey")
res29:
List[
String] =
List(hey, hey, hey, hey, hey)
//unzip方法
scala>
List.unzip(res20)
res30: (
List[Int],
List[Char]) = (
List(
1,
2,
3,
4),
List(
1,
2,
3,
4))
//list.flatten,将列表平滑成第一个无素
scala> val xss =
|
List(
List(
'a',
'b'),
List(
'c'),
List(
'd',
'e'))
xss:
List[
List[Char]] =
List(
List(a, b),
List(c),
List(d, e))
scala> xss.flatten
res31:
List[Char] =
List(a, b, c, d, e)
//列表链接
scala>
List.concat(
List(
'a',
'b'),
List(
'c'))
res32:
List[Char] =
List(a
, b, c)
(6)::和:::操做符介绍
List中经常使用'::',发音为"cons"。Cons把一个新元素组合到已有元素的最前端,而后返回结果List。
scala> val twoThree = List(2, 3)
scala> val oneTwoThree = 1 :: twoThree
scala> oneTwoThree
oneTwoThree: List[Int] = List(1, 2, 3)
上面表达式"1::twoThree"中,::是右操做数,列表twoThree的方法。可能会有疑惑。表达式怎么是右边参数的方法,这是Scala语言的一个例外的状况:若是一个方法操做符标注,如a * b,那么方法被左操做数调用,就像a.* (b)--除非方法名以冒号结尾。这种状况下,方法被右操做数调用。
List有个方法叫":::",用于实现叠加两个列表。
scala> val one =
List(
'A',
'B')
val one =
List(
'A',
'B')
scala> val two =
List(
'C',
'D')
scala> one:::two
res1:
List[Char] =
List(A, B, C, D)
集是不重复元素的结合。集不保留顺序,默认是以哈希集实现。
若是想要按照已排序的顺序来访问集中的元素,可使用SortedSet(已排序数据集),已排序的数据集是用红黑树实现的。
默认状况下,Scala 使用的是不可变集合,若是你想使用可变集合,须要引用 scala.collection.mutable.Set 包。
val set1 =Set("1","1","2","3")
println(set1.mkString(","))
若是咱们引入的集合的包是可变的,那么咱们建立的集合就是可变的
import scala.collection.mutable.Set
val set2 = Set(1, 2, 3)
set2.add(4)
set2 += 5
//使用.这个方法添加元素,会返回一个新的集合
val set3 = set2.+(6)
println(set2.mkString(","))
println(set3.mkString("\001"))
set3 -= 1
println(set3.mkString("."))
set3.remove(2)
println(set3.mkString("."))
for(x <- set3){
println(x )
}
注意:若是要建立有序的set,那么须要使用SortedSet。用法与Set相似
更多Set集合操做参见以下:
http://www.runoob.com/scala/scala-sets.html
序号 |
方法 |
描述 |
1 |
def +(elem: A): Set[A] |
为集合添加新元素,并建立一个新的集合,除非元素已存在 |
2 |
def -(elem: A): Set[A] |
移除集合中的元素,并建立一个新的集合 |
3 |
def contains(elem: A): Boolean |
若是元素在集合中存在,返回 true,不然返回 false。 |
4 |
def &(that: Set[A]): Set[A] |
返回两个集合的交集 |
5 |
def &~(that: Set[A]): Set[A] |
返回两个集合的差集 |
6 |
def ++(elems: A): Set[A] |
合并两个集合 |
7 |
def drop(n: Int): Set[A]] |
返回丢弃前n个元素新集合 |
8 |
def dropRight(n: Int): Set[A] |
返回丢弃最后n个元素新集合 |
9 |
def dropWhile(p: (A) => Boolean): Set[A] |
从左向右丢弃元素,直到条件p不成立 |
10 |
def max: A |
查找最大元素 |
11 |
def min: A |
查找最小元素 |
12 |
def take(n: Int): Set[A] |
返回前 n 个元素 |
咱们可使用map方法,传入一个函数,而后将这个函数做用在集合当中的每个元素上面
map:将集合中的每个元素映射到某一个函数
val listFunc = List("name","age","zhangsan","lisi")
println(listFunc.map(x => x +"hello"))
println(listFunc.map(_.toUpperCase()))
flatmap:flat即压扁,压平,扁平化,效果就是将集合中的每一个元素的子元素映射到某个函数并返回新的集合
val listFunc2 = List("address","phonNo")
println(listFunc2.flatMap( x => x +"WORLD"))
队列Queue是一个先进先出的结构
//建立可变的队列
val queue1 = new mutable.Queue[Int]()
println(queue1)
//队列当中添加元素
queue1 += 1
//队列当中添加List
queue1 ++=List(2,3,4)
println(queue1)
val dequeue = queue1.dequeue()
println(dequeue)
println(queue1)
//塞入元素到队列
queue1.enqueue(5,6,7)
println(queue1)
//获取第一个元素
println(queue1.head)
//获取最后一个元素
println(queue1.last)
将二元函数引用集合当中的函数
val reduceList = List(1,2,3,4,5)
//1-2-3-4-5 = -13
val reduceLeftList = reduceList.reduceLeft(_ - _)
val reduceLeftList2 = reduceList.reduceLeft((x,y) => x-y)
println(reduceLeftList)
println(reduceLeftList2)
//reduceRight操做
// 4-5 = -1
// 3- (-1) = 4
//2-4 = -2
//1 -(-2) = 3
val reduceRightList = reduceList.reduceRight(_ - _)
println(reduceRightList)
fold函数将上一步返回的值做为函数的第一个参数继续传递参与运算,直到list中的全部元素被遍历。能够把reduceLeft看作简化版的foldLeft。相关函数:fold,foldLeft,foldRight,能够参考reduce的相关方法理解。
reduce的本质其实就是fold操做,只不过咱们使用fold操做的时候,须要指定初始值
fold操做
val foldList = List(1,9,2,8)
val foldResult = foldList.fold(10)((x,y) => x+y)
println(foldResult)
foldLeft操做
//50-1-9-2-8 = 30
val foldLeftResult = foldList.foldLeft(50)((x,y) => x-y)
println(foldLeftResult)
对于多个List集合,咱们可使用Zip操做,将多个集合当中的值绑定到一块儿去
val zipList1 = List("name","age","sex")
val zipList2 = List("zhangsan",28)
val zip = zipList1.zip(zipList2)
val toMap1 = zip.toMap
println(zip)
println(toMap1)
对于集合当中的元素,咱们也可使用迭代器来进行遍历
val listIterator = List(1,2,"zhangsan")
val iterator = listIterator.iterator
while(iterator.hasNext){
println(iterator.next())
}
scala当中为了解决多线程并发的问题,提供对应的线程安全的集合
https://www.scala-lang.org/api/2.11.8/#package
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
大体了解便可
1) 若是想在变量名、类名等定义中使用语法关键字(保留字),能够配合反引号反引号:
val `val` = 42 |
2) 这种形式叫中置操做符,A操做符B等同于A.操做符(B)
3) 后置操做符,A操做符等同于A.操做符,若是操做符定义的时候不带()则调用时不能加括号
4) 前置操做符,+、-、!、~等操做符A等同于A.unary_操做符。
5) 赋值操做符,A操做符=B等同于A=A操做符B
函数能够做为一个参数传入到一个方法当中去
def main(args: Array[String]): Unit = {
val myFu(x:Int) =>{
x * x
}nc1 =
val myArray = Array(1,3,5,7,9).map(myFunc1)
println(myArray.mkString(","))
}
没有名字的函数便是匿名函数,咱们能够经过函数表达式来设置匿名函数
def main(args: Array[String]): Unit = {
println((x:Int,y:String) => x + y)
}
一、可以接受函数做为参数的方法,叫作高阶函数
def main(args: Array[String]): Unit = {
val func3:(Int,String) =>(String,Int)={
(x,y)=>(y,x)
}
def myMethod3(hello:(Int,String) => (String,Int)):Int ={
val resultFunc = hello(20,"hello")
resultFunc._2
}
println(myMethod3(func3))
}
二、高阶函数一样能够返回一个函数类型
def main(args: Array[String]): Unit = {
def myFunc4(x:Int) = (y:String) => y
println(myFunc4(50))
}
def main(args: Array[String]): Unit = {
val array = Array(1,2,3,4,5,6,7,8,9)
//map当中须要传入一个函数,咱们能够直接定义一个函数
array.map((x:Int) => x * 2 )
//进一步化简 参数推断省去类型信息
array.map((x) => x * 2 )
//进一步化简 单个参数能够省去括号
array.map( x => x * 2 )
//进一步化简 若是变量只在=>右边只出现一次,能够用_来代替
array.map( 2 * _ )
}
柯里化存在的意义是什么???注意:柯里化是scala当中面向函数式编程致使的一种必然的结果,最终推导而来产生的一种现象
def main(args: Array[String]): Unit = {
//柯里化的定义形式
def kery(x:Int)(y:Int):Int={
x + y
}
println(kery(3)(5))
//柯里化的推导过程,注意方法的返回值不要定义任何的返回值类型
val keryResult = (x:Int) => (y:Int) => {x + y}
def keryMethod(x:Int) ={
(y:Int) => x+ y
}
println(keryMethod(20))
println(keryMethod(20)(10))
//注意,方法当中的函数,调用了方法的参数,就叫作闭包
}
再来看一个案例
/**
* 柯里化的应用,比较数组集合当中两个对应下标字符串是否相等
* @param args
*/
def main(args: Array[String]): Unit = {
val a = Array("Hello", "World")
val b = Array("hello", "world")
println(a.corresponds(b)(_.equalsIgnoreCase(_)))
}
建立一个scala class来定义咱们的一个类。类当中能够定义各类属性或者方法,或者函数均可以
class Person {
//定义一个属性,叫作name的,使用val不可变量来进行修饰
// 用val修饰的变量是可读属性,有getter但没有setter(至关与Java中用final修饰的变量)
val name:String ="zhangsan"
//定义一个属性,叫作age的,使用var可变量来进行修饰
//用var修饰的变量都既有getter,又有setter
var age:Int = 28
//类私有字段,只能在类的内部使用或者伴生对象中访问
private val address:String = "地球上"
//类私有字段,访问权限更加严格的,该字段在当前类中被访问
//在伴生对象里面也不能够访问
private[this] var pet = "小强"
//在类当中定义了一个方法,
def hello(first:Int,second:String):Int ={
println(first+"\t"+second)
250
}
/**
* 定义了一个函数
*/
val func1 =(x:Int,y:Int) =>{
x+y
}
}
class Person{
|
若是想要使用类的话,那么REPL就知足不了咱们的要求了,咱们从新建立一个对应的Object的scala文件
object ScalaClass {
def main(args: Array[String]): Unit = {
//建立对象两种方式。这里都是使用的无参构造来进行建立对象的
val person = new Person
val person1 = new Person()
//注意,咱们可使用对象的属性加上_= 给var修饰的属性进行从新赋值
//其实就是调用set方法,方法名叫作 age_=
person.age_= (50)
//直接调用类的属性,其实就是调用get方法
println(person.age)
println(person.hello(50,"helloworld"))
val func = person.func1(10,20)
println(func)
println("============")
}
}
对于scala类中的每个属性,编译后,会有一个私有的字段和相应的getter、setter方法生成
//getter方法
println(person age)
//setter方法
println(person age_= (18))
//getter方法
println(person.age)
固然了,你也能够不使用自动生成的方式,本身定义getter和setter方法
class Dog2 { private var _leg = 4 def leg = _leg def leg_=(newLeg: Int) { _leg = newLeg } } |
使用之:
val dog2 = new Dog2 dog2.leg_=(10) println(dog2.leg) |
规范提示:本身手动建立变量的getter和setter方法须要遵循如下原则:
1) 字段属性名以“_”做为前缀,如:_leg
2) getter方法定义为:def leg = _leg
3) setter方法定义时,方法名为属性名去掉前缀,并加上后缀,后缀是:“leg_=”,如例子所示
scala当中类的构造器分为两种:主构造器和辅助构造器
scala当中规定,全部的辅助构造器,最后都必须调用另一个构造器,另一个构造器能够是辅助构造器,也能够是主构造器
//主构造器,直接定义在类上面
class Dog (name:String,age:Int){
//在scala当中,能够直接将代码写在class当中,而在java当中,
//代码必须包含在方法当中。
//其实在scala当中,虽然你把代码写在了Class类当中,通过编译以后,
//class类的代码都进入到了主构造器方法当中去了
println(name)
println(age)
var gender:String = "";
def this(name:String,age:Int,gender:String){
//每一个辅助构造器,都必须以其余辅助构造器,或者主构造器的调用做为第一句
this(name:String,age:Int)
this.gender = gender
}
var color ="";
/**
* 咱们也能够经过private来进行修饰咱们的构造器,
* @param name
* @param age
* @param color
* @param gender
*/
private def this(name:String,age:Int,color:String,gender:String){
this(name:String,age:Int)
this.color = color
}
}
在scala当中,没有相似于像java当中的static修饰的静态属性或者静态方法或者静态代码块之类的,可是咱们能够经过scala当中的Object来实现相似的功能。能够理解为scala当中的Object里面的属性或者方法都是静态的,能够直接调用
定义一个class类,而后在class类当中定义一个Object的对象。object对象当中的全部属性或者方法都是静态的
class Session {
def hello(first:Int):Int={
println(first)
first
}
}
object SessionFactory{
val session = new Session
def getSession():Session ={
session
}
def main(args: Array[String]): Unit = {
for(x <- 1 to 10){
//经过直接调用,产生的对象都是单列的
val session = SessionFactory.getSession()
println(session)
}
}
}
class ClassObject {
val id = 1
private var name = "itcast"
def printName(): Unit ={
//在Dog类中能够访问伴生对象Dog的私有属性
println(ClassObject.CONSTANT + name )
}
}
object ClassObject{
//伴生对象中的私有属性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new ClassObject
//访问私有的字段name
p.name = "123"
p.printName()
}
}
class ApplyObjectClass (name:String){
println(name)
}
object ApplyObjectClass{
def apply(name:String): ApplyObjectClass = {
new ApplyObjectClass(name)
}
def main(args: Array[String]): Unit = {
//调用的apply方法来建立对象
val applyObjectClass = ApplyObjectClass("lisi")
//调用的是new Class来建立对象
val applyObjectClass2 =new ApplyObjectClass("wangwu")
}
}
//1.在object中定义main方法
object Main_Demo1 {
def main(args: Array[String]) {
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World1!")
}
}
}
//2.使用继承App Trait ,将须要写在 main 方法中运行的代码
// 直接做为 object 的 constructor 代码便可,
// 并且还可使用 args 接收传入的参数。
object Main_Demo2 extends App{
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World2!")
}
}
Scala中没有枚举类型,可是咱们能够退经过定义一个扩展Enumeration类的对象,并以value调用初始化枚举中的全部可能值:
|
类内部 |
本包 |
子类 |
外部包 |
|
|
|
|
|
public |
√ |
√ |
√ |
√ |
|
|
|
|
|
protected |
√ |
√ |
√ |
× |
|
|
|
|
|
default |
√ |
√ |
× |
× |
|
|
|
|
|
private |
√ |
× |
× |
× |
|
|
|
|
|
package cn.itcast.extends_demo
class Person {
val name="super"
def getName=this.name
}
class Student extends Person{
//继承加上关键字
override
val name="sub"
//子类能够定义本身的field和method
val score="A"
def getScore=this.score
}
class Person1 {
private val name = "leo"
val age=50
def getName = this.name
}
class Student1 extends Person1{
private val score = "A"
//子类能够覆盖父类的 val field,使用override关键字
override
val age=30
def getScore = this.score
//覆盖父类非抽象方法,必需要使用 override 关键字
//同时调用父类的方法,使用super关键字
override def getName = "your name is " + super.getName
}
若是实例化了子类的对象,可是将其赋予了父类类型的变量,在后续的过程当中,又须要将父类类型的变量转换为子类类型的变量,应该如何作?
Scala |
Java |
obj.isInstanceOf[C] |
obj instanceof C |
obj.asInstanceOf[C] |
(C)obj |
classOf[C] |
C.class |
package cn.itcast.extends_demo
class Person3 {}
class Student3 extends Person3
object Student3{
def main (args: Array[String] ) {
val p: Person3 = new Student3
var s: Student3 = null
//若是对象是 null,则 isInstanceOf 必定返回 false
println (s.isInstanceOf[Student3])
// 判断 p 是否为 Student3 对象的实例
if (p.isInstanceOf[Student3] ) {
//把 p 转换成 Student3 对象的实例
s = p.asInstanceOf[Student3]
}
println (s.isInstanceOf[Student3] )
}
}
package cn.itcast.extends_demo
class Person4 {}
class Student4 extends Person4
object Student4{
def main(args: Array[String]) {
val p:Person4=new Student4
//判断p是否为Person4类的实例
println(p.isInstanceOf[Person4])//true
//判断p的类型是否为Person4类
println(p.getClass == classOf[Person4])//false
//判断p的类型是否为Student4类
println(p.getClass == classOf[Student4])//true
}
}
package cn.itcast.extends_demo
class Person5 {}
class Student5 extends Person5
object Student5{
def main(args: Array[String]) {
val p:Person5=new Student5
p match {
// 匹配是否为Person类或其子类对象
case per:Person5 => println("This is a Person5's Object!")
// 匹配全部剩余状况
case _ =>println("Unknown type!")
}
}
}
package cn.itcast.extends_demo
class Person6{
protected var name:String="tom"
protected[this] var hobby:String ="game"
protecteddef sayBye=println("再见...")
}
class Student6 extends Person6{
//父类使用protected 关键字来修饰 field能够直接访问
def sayHello =println("Hello "+name)
//父类使用protected 关键字来修饰method能够直接访问
def sayByeBye=sayBye
def makeFriends(s:Student6)={
println("My hobby is "+hobby+", your hobby is UnKnown")
}
}
object Student6{
def main(args: Array[String]) {
val s:Student6=new Student6
s.sayHello
s.makeFriends(s)
s.sayByeBye
}
}
package cn.itcast.extends_demo
class Person7(val name:String,val age:Int){
var score :Double=0.0
var address:String="beijing"
def this(name:String,score:Double)={
//每一个辅助constructor的第一行都必须调用其余辅助constructor或者主constructor代码
//主constructor代码
this(name,30)
this.score=score
}
//其余辅助constructor
def this(name:String,address:String)={
this(name,100.0)
this.address=address
}
}
class Student7(name:String,score:Double) extends Person7(name,score)
package cn.itcast.extends_demo
abstract class Person9(val name:String) {
//必须指出返回类型,否则默认返回为Unit
def sayHello:String
def sayBye:String
}
classStudent9(name:String) extends Person9(name){
//必须指出返回类型,否则默认
def sayHello: String = "Hello,"+name
def sayBye: String ="Bye,"+name
}
object Student9{
def main(args: Array[String]) {
val s = new Student9("tom")
println(s.sayHello)
println(s.sayBye)
}
}
package cn.itcast.extends_demo
abstract class Person10 (val name:String){
//抽象fields
val age:Int
}
class Student10(name: String) extends Person10(name) {
val age: Int = 50
}
package cn.itcast.triat
trait HelloTrait {
def sayHello(): Unit
}
trait MakeFriendsTrait {
def makeFriends(c: Children): Unit
}
//多重继承 trait
class Children(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable{
def sayHello() =println("Hello, " + this.name)
def makeFriends(c: Children) = println("Hello, my name is " + this.name + ", your name is " + c.name)
}
object Children{
def main(args: Array[String]) {
val c1=new Children("tom")
val c2=new Children("jim")
c1.sayHello()//Hello, tom
c1.makeFriends(c2)//Hello, my name is tom, your name is jim
}
}
package cn.itcast.triat
/**
* 好比 trait 中能够包含不少子类都通用的方法,例如打印日志或其余工具方法等等。
* spark就使用trait定义了通用的日志打印方法;
*/
trait Logger {
def log(message: String): Unit = println(message)
}
class PersonForLog(val name: String) extends Logger {
def makeFriends(other: PersonForLog) = {
println("Hello, " + other.name + "! My name is " + this.name + ", I miss you!!")
this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + "]")
}
}
object PersonForLog{
def main(args: Array[String]) {
val p1=new PersonForLog("jack")
val p2=new PersonForLog("rose")
p1.makeFriends(p2)
//Hello, rose! My name is jack, I miss you!!
//makeFriens method is invoked with parameter PersonForLog[name = rose]
}
}
package cn.itcast.triat
trait PersonForField {
val age:Int=50
}
//继承 trait 获取的field直接被添加到子类中
class StudentForField(val name: String) extends PersonForField {
def sayHello = println("Hi, I'm " + this.name + ", my age is "+ age)
}
object StudentForField{
def main(args: Array[String]) {
val s=new StudentForField("tom")
s.sayHello
}
}
package cn.itcast.triat
trait SayHelloTrait {
val msg:String
def sayHello(name: String) = println(msg + ", " + name)
}
class PersonForAbstractField(val name: String) extends SayHelloTrait {
//必须覆盖抽象 field
val msg = "Hello"
def makeFriends(other: PersonForAbstractField) = {
this.sayHello(other.name)
println("I'm " + this.name + ", I want to make friends with you!!")
}
}
object PersonForAbstractField{
def main(args: Array[String]) {
val p1=new PersonForAbstractField("Tom")
val p2=new PersonForAbstractField("Rose")
p1.makeFriends(p2)
}
}
package cn.itcast.triat
trait LoggedTrait {
// 该方法为实现的具体方法
def log(msg: String) = {}
}
trait MyLogger extends LoggedTrait{
// 覆盖 log() 方法
override def log(msg: String) = println("log: " + msg)
}
class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
def sayHello = {
println("Hi, I'm " + this.name)
log("sayHello method is invoked!")
}
}
object PersonForMixTraitMethod{
def main(args: Array[String]) {
val tom= new PersonForMixTraitMethod("Tom").sayHello //结果为:Hi, I'm Tom
// 使用 with 关键字,指定混入MyLogger trait
val rose = new PersonForMixTraitMethod("Rose") with MyLogger
rose.sayHello
// 结果为: Hi, I'm Rose
// 结果为: log: sayHello method is invoked!
}
}
package cn.itcast.triat
trait HandlerTrait {
def handle(data: String) = {println("last one")}
}
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check signature: " + data)
super.handle(data)
}
}
class PersonForRespLine(val name: String) extends SignatureValidHandlerTrait with DataValidHandlerTrait {
def sayHello = {
println("Hello, " + this.name)
this.handle(this.name)
}
}
object PersonForRespLine{
def main(args: Array[String]) {
val p=new PersonForRespLine("tom")
p.sayHello
//执行结果:
// Hello, tom
// check data: tom
// check signature: tom
// last one
}
}
package cn.itcast.triat
trait ValidTrait {
//抽象方法
def getName: String
//具体方法,具体方法的返回值依赖于抽象方法
def valid: Boolean = {"Tom".equals(this.getName)
}
}
class PersonForValid(val name: String) extends ValidTrait {
def getName: String = this.name
}
object PersonForValid{
def main(args: Array[String]): Unit = {
val person = new PersonForValid("Rose")
println(person.valid)
}
}
package cn.itcast.triat
class Person_One {
println("Person's constructor!")
}
trait Logger_One {
println("Logger's constructor!")
}
trait MyLogger_One extends Logger_One {
println("MyLogger's constructor!")
}
trait TimeLogger_One extends Logger_One {
println("TimeLogger's contructor!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("Student's constructor!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
//执行结果为:
// Person's constructor!
// Logger's constructor!
// MyLogger's constructor!
// TimeLogger's contructor!
// Student's constructor!
}
}
package cn.itcast.triat
class MyUtil {
def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
def sayHello {
this.log("Hi, I'm " + this.name)
this.printMsg("Hello, I'm " + this.name)
}
}
object Person_Three{
def main(args: Array[String]) {
val p=new Person_Three("Tom")
p.sayHello
//执行结果:
// log: Hi, I'm Tom
// Hello, I'm Tom
}
}
Scala有一个十分强大的模式匹配机制,能够应用到不少场合:如switch语句、类型检查等。而且Scala还提供了样例类,对模式匹配进行了优化,能够快速进行匹配。
def main(args: Array[String]): Unit = {
val charStr = '6'
charStr match {
case '+' => println("匹配上了加号")
case '-' => println("匹配上了减号")
case '*' => println("匹配上了乘号")
case '/' => println("匹配上了除号")
//注意。全部的模式匹配都必须最终匹配上一个值,若是没有匹配上任何值,就会报错
// case _ => println("都没有匹配上,我是默认值")
}
}
def main(args: Array[String]): Unit = {
val arr = Array("hadoop", "zookeeper", "spark")
val name = arr(Random.nextInt(arr.length))
name match {
case "hadoop" => println("大数据分布式存储和计算框架...")
case "zookeeper" => println("大数据分布式协调服务框架...")
case "spark" => println("大数据分布式内存计算框架...")
case _ => println("我不认识你...")
}
}
模式匹配当中,咱们也能够经过条件进行判断
def main(args: Array[String]): Unit = {
var ch = "500"
var sign = 0
ch match {
case "+" => sign = 1
case "-" => sign = 2
case _ if ch.equals("500") => sign = 3
case _ => sign = 4
}
println(ch + " " + sign)
}
注意在map当中会存在泛型擦除的状况。注意在进行非数组的类型匹配的时候,类型都会进行擦除
def main(args: Array[String]): Unit = {
//注意泛型擦除,在模式匹配当中的类型匹配中,除了Array类型觉得,全部的其余的数据类型都会被擦除掉
val a = 3
val obj = if(a == 1) 1
else if(a == 2) "2"
else if(a == 3) BigInt(3)
else if(a == 4) Map("aa" -> 1)
else if(a == 5) Map(1 -> "aa")
else if(a == 6) Array(1, 2, 3)
else if(a == 7) Array("aa", 1)
else if(a == 8) Array("aa")
val r1 = obj match {
case x: Int => x
case s: String => s.toInt
case BigInt => -1 //不能这么匹配
case _: BigInt => Int.MaxValue
case m: Map[String, Int] => "Map[String, Int]类型的Map集合"
case m: Map[_, _] => "Map集合"
case a: Array[Int] => "It's an Array[Int]"
case a: Array[String] => "It's an Array[String]"
case a: Array[_] => "It's an array of something other than Int"
case _ => 0
}
println(r1 + ", " + r1.getClass.getName)
}
def main(args: Array[String]): Unit = {
val arr = Array(0, 3, 5)
arr match {
case Array(0, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
//匹配数组以1 开始做为第一个元素
case Array(1, _*) => println("0 ...")
case _ => println("something else")
}
val lst = List(3, -1)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: tail => println("0 ...")
case _ => println("something else")
}
val tup = (1, 3, 7)
tup match {
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
注意:在Scala中列表要么为空(Nil表示空列表)要么是一个head元素加上一个tail列表。
9 :: List(5, 2) :: 操做符是将给定的头和尾建立一个新的列表
注意::: 操做符是右结合的,如9 :: 5 :: 2 :: Nil至关于 9 :: (5 :: (2 :: Nil))
样例类首先是类,除此以外它是为模式匹配而优化的类,样例类用case关键字进行声明。样例类主要是使用在咱们后面的sparkSQL当中,经过样例类来映射咱们的表当中的对象
定义形式:
case class 类型,是多例的,后面要跟构造参数。 case class Student(name:String)
case object 类型,是单例的。 case object Person
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
//1、样例类当中的主构造器参数默认为val的
//2、样例类当中的apply和unapply方法自动生成
object CaseDemo04 extends App {
val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))
arr(2) match {
case SubmitTask(id, name) => {
println(s"$id,$name")
println(id)
println(name)
println(id+"\t"+name)
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
被包在花括号内没有match的一组case语句是一个偏函数,它是PartialFunction[A, B]的一个实例,A表明输入参数类型,B表明返回结果类型,经常使用做输入模式匹配,偏函数最大的特色就是它只接受和处理其参数定义域的一个子集。
val func1: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
// case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("one"))
println(func2("one"))
//若是偏函数当中没有匹配上,那么就会报错,咱们能够经过isDefinedAt来进行判断
// println(func1("three"))
println(func1.isDefinedAt("three"))
if(func1.isDefinedAt("three")){
println("hello world")
}else{
println("world hello")
}
}
类型参数主要就是研究scala当中的类或者scala当中的方法的泛型
object Demo8 {
def main(args: Array[String]): Unit = {
val result1 = new MyClass("hello",50)
val result2 = new MyClass[Any,Any]("zhangsan","Lisi");
}
}
/**
* 定义一个class类,接收两个参数,可是两个参数都是泛型,泛型的类型,会根据咱们
* 建立类的实例化对象的时候,动态的传递进行动态的推断
* @param first
* @param second
* @tparam T
* @tparam B
*/
class MyClass[T,B](first:T,second:B){
println(first+","+second)
}
咱们的函数或者方法,也能够有类型参数
object methodType{
def getMiddle[T](canshu:T) ={
canshu
}
def main(args: Array[String]): Unit = {
// 从参数类型来推断类型
println(getMiddle(Array("Bob", "had", "a", "little", "brother")).getClass.getTypeName)
//指定类型,并保存为具体的函数。
val f = getMiddle[String] _
println(f("Bob"))
}
}
在scala当中,咱们能够经过上界或者下界来限定咱们泛型的类型,相似于java当中的
? extends T ?号就表示咱们使用的泛型,必须是T类型的子类,这种状况叫作上界
? super T ?号就表示咱们使用的泛型,必须是T类型的父类,这种状况叫作下界
在scala当中上界的表示方法使用的是 “<:”, 这个符号就是表示上界,这种形式称之为泛型的上界。
在scala当中下界的表示方式使用的是 “>:”, 这个符号就是表示下界,这种形式称之为泛型的下界
咱们能够经过上界的限定,限定咱们传入的类型必须是某个类型的子类
class Pair1[T <: Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
}
object Main1 extends App{
override def main(args: Array[String]): Unit = {
val p = new Pair1("hello", "Brooks")
println(p.smaller)
}
}
咱们能够经过下界的限定,限定咱们传入的类型必须是某个类型的父类
class Pair2[T](val first: T, val second: T) {
def replaceFirst[R >: T](newFirst: R) = new Pair2[R](newFirst, second)
override def toString = "(" + first + "," + second + ")"
}
object Main2 extends App{
override def main(args: Array[String]): Unit = {
val p = new Pair2("Nick", "Alice")
println(p)
println(p.replaceFirst("Joke"))
println(p)
}
}
在Java中,T同时是A和B的子类型,称之为多界,形式如:<T extends A & B>。
在Scala中,对上界和下界不能有多个,可是可使用混合类型,如:[T <: A with B]。
在Java中,不支持下界的多界形式。如:<T super A & B>这是不支持的。
在Scala中,对复合类型依然可使用下界,如:[T >: A with B]。
说白了就是将咱们的泛型转化成了具体的类型
在Scala中,若是你想标记某一个泛型能够隐式的转换为另外一个泛型,可使用:[T <% Comparable[T]],因为Scala的Int类型没有实现Comparable接口,因此咱们须要将Int类型隐式的转换为RichInt类型,好比:
咱们若是须要比较两个值的大小,那么咱们的两个值必须是Comparable的子类,那么咱们可使用泛型 T <% Comparable 来限制咱们泛型必须是Comparable的子类,而且咱们的泛型在执行真正比较的方法的时候,会根据咱们传入的类型,自动推断,进行隐式的转换,例如咱们传入4,2 进行比较,那么咱们会将4, 2 这两个类型作自动推断,转换成真正的RichInt类型而后再继续进行比较
/**
* 使用 <% 来实现咱们类型的隐式转换
* @param first
* @param second
* @tparam T
*/
class Pair3[T <% Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
override def toString = "(" + first + "," + second + ")"
}
object Main3 extends App {
val p = new Pair3(4, 2)
println(p.smaller)
}
协变和逆变主要是用来解决参数化类型的泛化问题。Scala的协变与逆变是很是有特点的,彻底解决了Java中泛型的一大缺憾;举例来讲,Java中,若是有 A是 B的子类,但 Card[A] 却不是 Card[B] 的子类;而 Scala 中,只要灵活使用协变与逆变,就能够解决此类 Java 泛型问题;
因为参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型是否也能够泛化呢?Java中这种状况下是不可泛化的,然而Scala提供了三个选择,即协变(“+”)、逆变(“-”)和非变。
下面说一下三种状况的含义,首先假设有参数化特征Queue,那它能够有以下三种定义。
(1) trait Queue[T] {}
这是非变状况。这种状况下,当类型B是类型A的子类型,则Queue[B]与Queue[A]没有任何从属关系,这种状况是和Java同样的。
(2) trait Queue[+T] {}
这是协变状况。这种状况下,当类型B是类型A的子类型,则Queue[B]也能够认为是Queue[A]的子类型,即Queue[B]能够泛化为Queue[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,因此称为协变。
(3) trait Queue[-T] {}
这是逆变状况。这种状况下,当类型B是类型A的子类型,则Queue[A]反过来能够认为是Queue[B]的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,因此称为逆变。
class Anmial
class Dog extends Anmial
协变
List[Dog] extends List[Anmial]
逆变
List[Anmial] extends List[Dog]
不变
List[Dog] = List[Dog]
package cn.itcast.scala.enhance.covariance
class Super
class Sub extends Super
//协变
class Temp1[+A](title: String)
//逆变
class Temp2[-A](title: String)
//非变
class Temp3[A](title: String)
object Covariance_demo{
def main(args: Array[String]) {
//支持协变 Temp1[Sub]仍是Temp1[Super]的子类
val t1: Temp1[Super] = new Temp1[Sub]("hello scala!!!")
//支持逆变 Temp1[Super]是Temp1[Sub]的子类
val t2: Temp2[Sub] = new Temp2[Super]("hello scala!!!")
//支持非变 Temp3[Super]与Temp3[Sub]没有从属关系,以下代码会报错
//val t3: Temp3[Sub] = new Temp3[Super]("hello scala!!!")
//val t4: Temp3[Super] = new Temp3[Sub]("hello scala!!!")
println(t1.toString)
println(t2.toString)
}
}
注:Scala Actor是scala 2.10.x版本及之前版本的Actor。
Scala在2.11.x版本中将Akka加入其中,做为其默认的Actor,老版本的Actor已经废弃。
①Scala中的并发编程思想与Java中的并发编程思想彻底不同,Scala中的Actor是一种不共享数据,依赖于消息传递的一种并发编程模式, 避免了死锁、资源争夺等状况。在具体实现的过程当中,Scala中的Actor会不断的循环本身的邮箱,并经过receive偏函数进行消息的模式匹配并进行相应的处理。
②若是Actor A和 Actor B要相互沟通的话,首先A要给B传递一个消息,B会有一个收件箱,而后B会不断的循环本身的收件箱, 若看见A发过来的消息,B就会解析A的消息并执行,处理完以后就有可能将处理的结果经过邮件的方式发送给A
Scala中的Actor可以实现并行编程的强大功能,它是基于事件模型的并发机制,Scala是运用消息的发送、接收来实现高并发的。
Actor能够看做是一个个独立的实体,他们之间是毫无关联的。可是,他们能够经过消息来通讯。一个Actor收到其余Actor的信息后,它能够根据须要做出各类相应。消息的类型能够是任意的,消息的内容也能够是任意的。
对于Java,咱们都知道它的多线程实现须要对共享资源(变量、对象等)使用synchronized 关键字进行代码块同步、对象锁互斥等等。并且,经常一大块的try…catch语句块中加上wait方法、notify方法、notifyAll方法是让人很头疼的。缘由就在于Java中多数使用的是可变状态的对象资源,对这些资源进行共享来实现多线程编程的话,控制好资源竞争与防止对象状态被意外修改是很是重要的,而对象状态的不变性也是较难以保证的。
与Java的基于共享数据和锁的线程模型不一样,Scala的actor包则提供了另一种不共享任何数据、依赖消息传递的模型,从而进行并发编程。
一、首先调用start()方法启动Actor
二、调用start()方法后其act()方法会被执行
三、向Actor发送消息
四、act方法执行完成以后,程序会调用exit方法
! |
发送异步消息,没有返回值。 |
!? |
发送同步消息,等待返回值。 |
!! |
发送异步消息,返回值是 Future[Any]。 |
注意:Future 表示一个异步操做的结果状态,可能尚未实际完成的异步任务的结果。
Any 是全部类的超类,Future[Any]的泛型是异步操做结果的类型。
注意:若是要开发scala的actor的代码,那么须要咱们将scala的SDK添加到咱们的项目当中去
怎么实现actor并发编程:
一、定义一个class或者是object继承Actor特质,注意导包import scala.actors.Actor
二、重写对应的act方法
三、调用Actor的start方法执行Actor
四、当act方法执行完成,整个程序运行结束
import scala.actors.Actor
class Actor1 extends Actor{
override def act(): Unit = {
for(i <- 1 to 10){
println("actor1====="+i)
}
}
}
object Actor2 extends Actor{
override def act(): Unit = {
for(j <- 1 to 10){
println("actor2====="+j)
}
}
}
object Actor1{
def main(args: Array[String]): Unit = {
val actor = new Actor1
actor.act()
Actor2.act()
}
}
说明:上面分别调用了两个单例对象的start()方法,他们的act()方法会被执行,相同与在java中开启了两个线程,线程的run()方法会被执行
注意:这两个Actor是并行执行的,act()方法中的for循环执行完成后actor程序就退出
怎么实现actor发送、接受消息
一、定义一个class或者是object继承Actor特质,注意导包import scala.actors.Actor
二、重写对应的act方法
三、调用Actor的start方法执行Actor
四、经过不一样发送消息的方式对actor发送消息
五、act方法中经过receive方法接受消息并进行相应的处理
六、act方法执行完成以后,程序退出
import scala.actors.Actor
class MyActor2 extends Actor{
override def act(): Unit = {
receive{
case "start" => println("starting......")
// case _ => println("我没有匹配到任何消息")
}
}
}
object MyActor2{
def main(args: Array[String]): Unit = {
val actor = new MyActor2
actor.start()
actor ! "start"
}
}
怎么实现actor能够不断地接受消息:
在act方法中可使用while(true)的方式,不断的接受消息。
class MyActor3 extends Actor{
override def act(): Unit = {
while (true){
receive{
case "start" => println("starting")
case "stop" =>println("stopping")
}
}
}
}
object MyActor3{
def main(args: Array[String]): Unit = {
val actor = new MyActor3
actor.start()
actor ! "start"
actor ! "stop"
}
}
说明:在act()方法中加入了while (true) 循环,就能够不停的接收消息
注意:发送start消息和stop的消息是异步的,可是Actor接收到消息执行的过程是同步的按顺序执行
使用react方法代替receive方法去接受消息
好处:react方式会复用线程,避免频繁的线程建立、销毁和切换。比receive更高效
注意: react 若是要反复执行消息处理,react外层要用loop,不能用while
class MyActor4 extends Actor{
override def act(): Unit = {
loop{
react{
case "start" => println("starting")
case "stop" => println("stopping")
}
}
}
}
object MyActor4{
def main(args: Array[String]): Unit = {
val actor = new MyActor4
actor.start()
actor ! "start"
actor ! "stop"
}
}
结合case class样例类发送消息和接受消息
一、将消息封装在一个样例类中
二、经过匹配不一样的样例类去执行不一样的操做
三、Actor能够返回消息给发送方。经过sender方法向当前消息发送方返回消息
case class AsyncMessage(id:Int,message:String)
case class SyncMessage(id:Int,message:String)
case class ReplyMessage(id:Int,message:String)
class MyActor5 extends Actor{
override def act(): Unit = {
loop{
react{
case AsyncMessage(id,message) => {
println(s"$id,$message")
sender ! ReplyMessage(2,"异步有返回值的消息处理成功")
}
case SyncMessage(id,message) =>{
println(s"$id,$message")
sender ! ReplyMessage(id,"我是同步消息的返回值,等到我返回以后才能继续下一步的处理")
}
}
}
}
}
object MyActor5{
def main(args: Array[String]): Unit = {
val actor: MyActor5 = new MyActor5
actor.start()
actor ! AsyncMessage(1,"helloworld")
val asyncMessage: Future[Any] = actor !! AsyncMessage(2,"actorSend")
val apply: Any = asyncMessage.apply()
println(apply)
println("helloworld22222")
//同步阻塞消息
val syncMessage: Any = actor !? SyncMessage(3,"我是同步阻塞消息")
println(syncMessage)
}
}
需求:
用actor并发编程写一个单机版的WordCount,将多个文件做为输入,计算完成后将多个任务汇总,获得最终的结果。
大体的思想步骤:
一、经过loop +react 方式去不断的接受消息
二、利用case class样例类去匹配对应的操做
三、其中scala中提供了文件读取的接口Source,经过调用其fromFile方法去获取文件内容
四、将每一个文件的单词数量进行局部汇总,存放在一个ListBuffer中
五、最后将ListBuffer中的结果进行全局汇总。
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.{BufferedSource, Source}
case class FileName(path: String)
case class ResultTask(mapWithWord: Map[String, Int])
class WordCount extends Actor {
override def act(): Unit = {
loop {
react {
//使用loop + react的方式接受咱们的数据
case FileName(path: String) => {
//使用Source来读取文件内容
val file: BufferedSource = Source.fromFile(path)
//获取文件全部内容
val fileContent: String = file.mkString
// println(fileContent)
//对文件内容进行切分
val split: Array[String] = fileContent.split("\r\n")
// println(split.toBuffer)
//对每一行进行按照空格进行切分
// val map: Array[Array[String]] = split.map(x => x.split(" "))
//切分以后,将数据进行压平
// val flatten: Array[String] = map.flatten
val flatten: Array[String] = split.flatMap(x => x.split(" "))
val map1: Array[(String, Int)] = flatten.map(x => (x, 1))
// println(map1.toBuffer)
val byKey: Map[String, Array[(String, Int)]] = map1.groupBy(x => x._1)
val values: Map[String, Int] = byKey.mapValues(x => x.length)
sender ! ResultTask(values)
}
}
}
}
}
object WordCount {
def main(args: Array[String]): Unit = {
//申明一个变量,存放咱们的结果数据
val resultTasks = new ListBuffer[ResultTask]
//申明一个set集合用于存放咱们异步发送的返回消息值
val futureSet: mutable.HashSet[Future[Any]] = new mutable.HashSet[Future[Any]]()
//定义咱们须要统计的数据文件路径
val files: Array[String] = Array("F:\\scala与spark课件资料教案\\2、scala次日\\wordCount\\1.txt", "F:\\scala与spark课件资料教案\\2、scala次日\\wordCount\\2.txt", "F:\\scala与spark课件资料教案\\2、scala次日\\wordCount\\3.txt")
//循环遍历咱们的数据文件,而后进行发送
for (f <- files) {
val count: WordCount = new WordCount
count.start();
val value: Future[Any] = count !! FileName(f)
futureSet.add(value)
}
while (futureSet.size > 0) {
//过滤咱们的set集合,只取那些有值的set集合
val completeFuture: mutable.HashSet[Future[Any]] = futureSet.filter(x => x.isSet)
for (future <- completeFuture) {
// 调用apply方法,获取到咱们的future实例,实际上就是ResultTask
val futureApply: Any = future.apply()
//判断咱们的结果值若是是ResultTask类型的话,那么咱们就添加到咱们的ListBuffer当中去,表示已经获取到了返回结果
resultTasks += futureApply.asInstanceOf[ResultTask]
//添加完ListBuffer以后,将set集合当中的元素减小,以便于退出while循环
futureSet -= future
}
}
println(resultTasks)
val flatten: ListBuffer[(String, Int)] = resultTasks.map(x => x.mapWithWord).flatten
val by: Map[String, ListBuffer[(String, Int)]] = flatten.groupBy( x => x._1)
println(by)
//第一个下划线表示咱们累加以后的结果
// 第二个下划线表示咱们集合当中每个元组
// _2 表示元组当中第二个元素
val values: Map[String, Int] = by.mapValues(x => x.foldLeft(0)( _ + _._2))
for((k,v) <- values){
println(k+"====>"+v)
}
}
}
def main(args: Array[String]): Unit = {
//注意文件的编码格式,若是编码格式不对,那么读取报错
val file: BufferedSource = Source.fromFile("F:\\scala与spark课件资料教案\\3、scala第三天\\files\\file.txt","GBK");
val lines: Iterator[String] = file.getLines()
for(line <- lines){
println(line)
}
//注意关闭文件
file.close()
}
若是要将文件内容转数组,直接调用toArray便可
若是想将以某个字符或某个正则表达式分开的字符成组读取,能够这么作:
def main(args: Array[String]): Unit = {
val file: BufferedSource = Source.fromFile("F:\\scala与spark课件资料教案\\3、scala第三天\\files\\file2.txt","GBK");
val split: Array[String] = file.mkString.split(" ")
println(split.mkString("\t"))
file.close()
}
一、读取网络资源
def main(args: Array[String]): Unit = {
val source: BufferedSource = Source.fromURL("http://www.baidu.com")
val string: String = source.mkString
println(string)
source.close()
}
二、文件写入操做
def main(args: Array[String]): Unit = {
val writer = new PrintWriter("F:\\scala与spark课件资料教案\\3、scala第三天\\files\\printWriter.txt")
for(i <- 1 to 100){
writer.println(i)
writer.flush()
}
writer.close()
}
三、控制台交互操做
def main(args: Array[String]): Unit = {
//控制台交互--老API
print("请输入内容:")
val consoleLine1 = Console.readLine()
println("刚才输入的内容是:" + consoleLine1)
//控制台交互--新API
print("请输入内容(新API):")
val consoleLine2 = StdIn.readLine()
println("刚才输入的内容是:" + consoleLine2)
}
@SerialVersionUID(1L)
class Person extends Serializable{
override def toString = name + "," + age
val name = "Nick"
val age = 20
}
object PersonMain extends App{
override def main(args: Array[String]): Unit = {
import java.io.{FileOutputStream, FileInputStream, ObjectOutputStream, ObjectInputStream}
val nick = new Person
val out = new ObjectOutputStream(new FileOutputStream("Nick.obj"))
out.writeObject(nick)
out.close()
val in = new ObjectInputStream(new FileInputStream("Nick.obj"))
val saveNick = in.readObject()
in.close()
println(saveNick)
}
}
咱们能够经过正则表达式匹配一个句子中全部符合匹配的内容,并输出:
def main(args: Array[String]): Unit = {
import scala.util.matching.Regex
val pattern1 = new Regex("(S|s)cala")
val pattern2 = "(S|s)cala".r
val str = "Scala is scalable and cool"
println((pattern2 findAllIn str).mkString(","))
}
Scala提供的隐式转换和隐式参数功能,是很是有特点的功能。是Java等编程语言所没有的功能。它能够容许你手动指定,将某种类型的对象转换成其余类型的对象或者是给一个类增长方法。经过这些功能,能够实现很是强大、特殊的功能。
Scala的隐式转换,其实最核心的就是定义隐式转换方法,即implicit conversion function。定义的隐式转换方法,只要在编写的程序内引入,就会被Scala自动使用。Scala会根据隐式转换方法的签名,在程序中使用到隐式转换方法接收的参数类型定义的对象时,会自动将其传入隐式转换方法,转换为另一种类型的对象并返回。这就是“隐式转换”。其中全部的隐式值和隐式方法必须放到object中。
然而使用Scala的隐式转换是有必定的限制的,总结以下:
所谓的隐式参数,指的是在函数或者方法中,定义一个用implicit修饰的参数,此时Scala会尝试找到一个指定类型的,用implicit修饰的参数,即隐式值,并注入参数。
Scala会在两个范围内查找:
(1)Scala默认会使用两种隐式转换,一种是源类型或者目标类型的伴生对象内的隐式转换方法;一种是当前程序做用域内的能够用惟一标识符表示的隐式转换方法。
(2)若是隐式转换方法不在上述两种状况下的话,那么就必须手动使用import语法引入某个包下的隐式转换方法,好比import test._。一般建议,仅仅在须要进行隐式转换的地方,用import导入隐式转换方法,这样能够缩小隐式转换方法的做用域,避免不须要的隐式转换。
(1)当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换
(2)当方法中的参数的类型与目标类型不一致时
object Chapter14 {
implicit def ConvertDoubleToInt(first:Double):Int= first.toInt
}
object Convert{
//导入隐式转换的方法
import Chapter14._
def main(args: Array[String]): Unit = {
val first:Int = 3.5
}
}
例如咱们也能够定义猫和狗,而且让狗学会抓老鼠的技能
object CatAndDog {
implicit def dogCatchMouse(dog:Dog) = new Cat()
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.catMouse("大黄狗")
}
}
class Cat{
def catMouse(name:String): Unit ={
println(name+"catch a mouse")
}
}
class Dog{
def wangwangwang(name:String) ={
println(name+"看门汪汪汪")
}
}
import java.io.File
import scala.io.Source
object MyPredef{
//定义隐式转换方法
implicit def file2RichFile(file: File)=new RichFile(file)
}
class RichFile(val f:File) {
def read()=Source.fromFile(f,"GBK").mkString
}
object RichFile{
def main(args: Array[String]) {
val f=new File("F:\\scala与spark课件资料教案\\3、scala第三天\\files\\file.txt")
//使用import导入隐式转换方法
import MyPredef._
//经过隐式转换,让File类具有了RichFile类中的方法
val content=f.read()
println(content)
}
}
class Man(val name:String)
class SuperMan(val name: String) {
def heat=print("超人打怪兽")
}
object SuperMan{
//隐式转换方法
implicit def man2SuperMan(man:Man)=new SuperMan(man.name)
def main(args: Array[String]) {
val hero=new Man("hero")
//Man具有了SuperMan的方法
hero.heat
}
}
class A(c:C) {
def readBook(): Unit ={
println("A说:好书好书...")
}
}
class B(c:C){
def readBook(): Unit ={
println("B说:看不懂...")
}
def writeBook(): Unit ={
println("B说:不会写...")
}
}
class C
object AB{
//建立一个类的2个类的隐式转换
implicit def C2A(c:C)=new A(c)
implicit def C2B(c:C)=new B(c)
}
object B{
def main(args: Array[String]) {
//导包
//1. import AB._ 会将AB类下的全部隐式转换导进来
//2. import AB._C2A 只导入C类到A类的的隐式转换方法
//3. import AB._C2B 只导入C类到B类的的隐式转换方法
import AB._
val c=new C
//因为A类与B类中都有readBook(),只能导入其中一个,不然调用共同方法时代码报错
//c.readBook()
//C类能够执行B类中的writeBook()
c.writeBook()
}
}
object Company{
//在object中定义隐式值 注意:同一类型的隐式值只容许出现一次,不然会报错
implicit val aaa="zhangsan"
implicit val bbb=10000.00
}
class Boss {
//注意参数匹配的类型 它须要的是String类型的隐式值
def callName()(implicit name:String):String={
name+" is coming !"
}
//定义一个用implicit修饰的参数
//注意参数匹配的类型 它须要的是Double类型的隐式值
def getMoney()(implicit money:Double):String={
" 当月薪水:"+money
}
}
object Boss extends App{
//使用import导入定义好的隐式值,注意:必须先加载不然会报错
import Company._
val boss =new Boss
println(boss.callName()+boss.getMoney())
}
目前大多数的分布式架构底层通讯都是经过RPC实现的,RPC框架很是多,好比前咱们学过的Hadoop项目的RPC通讯框架,可是Hadoop在设计之初就是为了运行长达数小时的批量而设计的,在某些极端的状况下,任务提交的延迟很高,因此Hadoop的RPC显得有些笨重。
Spark 的RPC是经过Akka类库实现的,Akka用Scala语言开发,基于Actor并发模型实现,Akka具备高可靠、高性能、可扩展等特色,使用Akka能够轻松实现分布式RPC功能。
Akka基于Actor模型,提供了一个用于构建可扩展的(Scalable)、弹性的(Resilient)、快速响应的(Responsive)应用程序的平台。
Actor模型:在计算机科学领域,Actor模型是一个并行计算(Concurrent Computation)模型,它把actor做为并行计算的基本元素来对待:为响应一个接收到的消息,一个actor可以本身作出一些决策,如建立更多的actor,或发送更多的消息,或者肯定如何去响应接收到的下一个消息。
Actor是Akka中最核心的概念,它是一个封装了状态和行为的对象,Actor之间能够经过交换消息的方式进行通讯,每一个Actor都有本身的收件箱(Mailbox)。经过Actor可以简化锁及线程管理,能够很是容易地开发出正确地并发程序和并行系统,Actor具备以下特性:
(1)、提供了一种高级抽象,可以简化在并发(Concurrency)/并行(Parallelism)应用场景下的编程开发
(2)、提供了异步非阻塞的、高性能的事件驱动编程模型
(3)、超级轻量级事件处理(每GB堆内存几百万Actor)
利用Akka的actor编程模型,实现2个进程间的通讯。
ActorSystem:在Akka中,ActorSystem是一个重量级的结构,他须要分配多个线程,因此在实际应用中,ActorSystem一般是一个单例对象,咱们可使用这个ActorSystem建立不少Actor。
注意:
(1)、ActorSystem是一个进程中的老大,它负责建立和监督actor
(2)、ActorSystem是一个单例对象
(3)、actor负责通讯
在Akka中,Actor负责通讯,在Actor中有一些重要的生命周期方法。
(1)preStart()方法:该方法在Actor对象构造方法执行后执行,整个Actor生命周期中仅执行一次。
(2)receive()方法:该方法在Actor的preStart方法执行完成后执行,用于接收消息,会被反复执行。
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<scala.compat.version>2.11</scala.compat.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.14</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.11</artifactId>
<version>2.3.14</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<!-- 限制jdk的编译版本插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
//todo:利用akka的actor模型实现2个进程间的通讯-----Master端
class Master extends Actor{
//构造代码块先被执行
println("master constructor invoked")
//prestart方法会在构造代码块执行后被调用,而且只被调用一次
override def preStart(): Unit = {
println("preStart method invoked")
}
//receive方法会在prestart方法执行后被调用,表示不断的接受消息
override def receive: Receive = {
case "connect" =>{
println("a client connected")
//master发送注册成功信息给worker
sender ! "success"
}
}
}
object Master{
def main(args: Array[String]): Unit = {
val host = args(0)
val port = args(1)
// val host="localhost"
// val port = 8888
//准备配置文件信息
val configStr=
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
//配置config对象 利用ConfigFactory解析配置文件,获取配置信息
val config=ConfigFactory.parseString(configStr)
// 1、建立ActorSystem,它是整个进程中老大,它负责建立和监督actor,它是单例对象
val masterActorSystem = ActorSystem("masterActorSystem",config)
// 2、经过ActorSystem来建立master actor
val masterActor: ActorRef = masterActorSystem.actorOf(Props(new Master),"masterActor")
// 3、向master actor发送消息
masterActor ! "connect"
}
}
注意:若是使用参数传递的方式来接收参数,那么必须在idea当中配置程序运行传递的参数
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
//todo:利用akka中的actor实现2个进程间的通讯-----Worker端
class Worker extends Actor{
println("Worker constructor invoked")
//prestart方法会在构造代码块以后被调用,而且只会被调用一次
override def preStart(): Unit = {
println("preStart method invoked")
//获取master actor的引用
//ActorContext全局变量,能够经过在已经存在的actor中,寻找目标actor
//调用对应actorSelection方法,
// 方法须要一个path路径:1、通讯协议、2、master的IP地址、3、master的端口 4、建立master actor老大 5、actor层级
val master: ActorSelection = context.actorSelection("akka.tcp://masterActorSystem@127.0.0.1:8888/user/masterActor")
//向master发送消息
master ! "connect"
}
//receive方法会在prestart方法执行后被调用,不断的接受消息
override def receive: Receive = {
case "connect" =>{
println("a client connected")
}
case "success" =>{
println("注册成功")
}
}
}
object Worker{
def main(args: Array[String]): Unit = {
//定义worker的IP地址
val host=args(0)
//定义worker的端口
val port=args(1)
//准备配置文件
val configStr=
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
//经过configFactory来解析配置信息
val config=ConfigFactory.parseString(configStr)
// 1、建立ActorSystem,它是整个进程中的老大,它负责建立和监督actor
val workerActorSystem = ActorSystem("workerActorSystem",config)
// 2、经过actorSystem来建立 worker actor
val workerActor: ActorRef = workerActorSystem.actorOf(Props(new Worker),"workerActor")
//向worker actor发送消息
workerActor ! "connect"
}
}
使用Akka实现一个简易版的spark通讯框架
需求实现逻辑
一、 启动master和worker
二、 在worker端对应的preStart方法中拿到master的引用对象,经过这个master引用向master发送注册信息,注册信息包含workerId, workCores, workMemory等信息
三、 master接受worker注册信息,保存注册信息在一个map集合当中,key为workerId,value为注册信息样例类。master将worker注册成功的信息反馈给worker端
四、 worker接受master反馈的注册成功信息,定时向master发送心跳信息。发送心跳信息,证实worker还活着
五、 master接受worker心跳信息,定时检查超时worker,并从map当中移除掉超时的worker节点信息
① Master类
package cn.itcast.spark |
② Worker类
package cn.itcast.spark
|
③ WorkerInfo类
package cn.itcast.spark
|
④ 样例类
package cn.itcast.spark |