目录
前言:
Scala是一门很费脑子的语言,不是必要的话,请谨慎入坑(个人感受)
Scala
一:Scala介绍以及Scala安装使用
1.1 Scala6个特征。
1).Java和scala可以混编
2).类型推测(自动推测类型)
3).并发和分布式(Actor)
4).特质,特征(类似java中interfaces 和 abstract结合)
5).模式匹配(类似java switch)
6).高阶函数
1.2 windows安装,配置环境变量
- 官网下载:http://www.scala-lang.org/download/2.10.4.html
- 下载好后安装。双击msi包安装,记住安装的路径。
- 配置环境变量(和配置jdk一样)
- 新建SCALA_HOME
- 上个步骤完成后,编辑Path变量,在后面追加如下:;%SCALA_HOME%\bin;%SCALA_HOME%\jre\bin
- 打开cmd,输入:scala - version 看是否显示版本号,确定是否安装成功
- idea 中配置scala插件
1)打开idea,close项目后,点击Configure->Plugins
2)搜索scala,点击Install安装
3)设置jdk,打开Project Structure,点击new 选择安装好的jdk路径
4)创建scala项目,配置scala sdk(Software Development Kit)
补充:如何在项目中运行一个scala代码:new 新建一个Scala的object而不是class
1.3 linux环境下scala的安装
访问Scala官网(官网),下载Scala。登录后,官网会自动识别你的操作系统类型,如果是Linux操作系统,
官网会自动提供.tgz格式的安装包,比如,笔者于2016年10月31日登录官网,可以下载到scala-2.11.8.tgz(文件大小是27.3MB)。
这里假设scala-2.11.8.tgz被下载到“~/下载/”目录下面,其中,“~”表示当前用户的工作目录,比如,
如果当前你正在使用用户名hadoop登录Linux系统,那么,当前用户的工作目录就是“/home/hadoop/”。
在Linux操作系统中安装Scala的过程如下。
首先要指定Scala的安装目录,比如,这里我们选择安装在“/usr/local/”目录下,这里假设当前用户登录名是hadoop。
然后,把刚才下载的scala-2.11.8.tgz文件解压缩到“/usr/local/”目录下,修改文件夹名称,并为hadoop用户赋予权限,具体如下:
sudo tar -zxf ~/下载/scala-2.11.8.tgz -C /usr/local # 解压到/usr/local中
cd /usr/local/
sudo mv ./scala-2.11.8/ ./scala # 将文件夹名改为scala
sudo chown -R hadoop ./scala # 修改文件权限,用hadoop用户拥有对scala目录的权限
Shell 命令
接着需要把scala命令添加到path环境变量中。这里我们在 ~/.bashrc 中进行设置。可以采用vim编辑器打开.bashrc文件:
vim ~/.bashrc
Shell 命令
打开vim编辑器以后,需要键盘敲击输入一个字母i,进入编辑状态,然后才能修改内容。
然后,在.bashrc文件的最开头位置,修改path环境变量设置,把scala命令所在的目录“/usr/local/scala/bin”增加到path中,具体如下:
export PATH=$PATH:/usr/local/scala/bin
注意,上面的PATH和等号之间,不要加入任何空格,否则会出错。
修改后,保存退出(方法是:首先,按键盘Esc键,退出vim的编辑状态,然后,敲击键盘输入“:wq”三个英文字母,然后回车,即可保存退出)。
接着还需要让该环境变量生效,执行如下代码:
source ~/.bashrc # 使变量设置生效
Shell 命令
设置好后我们来检验一下是否设置正确,可以输入scala命令:
scala
Shell 命令
输入scala命令以后,屏幕上显示scala和Java版本信息,并进入“scala>”提示符状态,就可以开始使用Scala解释器了,你就可以输入scala语句来调试程序代码了。
二:Scala基础
2.1 数据类型
2.2 变量和常量的声明
1)定义变量
- 定义变量或者常量的时候,也可以写上返回的类型,一般省略,如:val a:Int = 10
- 常量不可再赋值
变量 :用 var 定义 ,可修改
常量 :用 val 定义,不可修改
/**
* 定义变量和常量
* 变量 :用 var 定义 ,可修改
* 常量 :用 val 定义,不可修改
*/
var name = "zhangsan"
println(name)
name ="lisi"
println(name)
val gender = "m"
//gender = "m"//错误,不能给常量再赋值
2)创建对象
class Person{
val name = "zhangsan"
val age = 18
def sayName() = {
"my name is "+ name
}
}
object Lesson_Class {
def main(args: Array[String]): Unit = {
val person = new Person()
println(person.age);
println(person.sayName())
}
}
3)apply :如果在创建Object时传入参数,那么会自动根据参数的个数去Object中寻找相应的apply方法
/**
* object 单例对象中不可以传参,
* 如果在创建Object时传入参数,那么会自动根据参数的个数去Object中寻找相应的apply方法
*/
object Lesson_ObjectWithParam {
def apply(s:String) = {
println("name is "+s)
}
def apply(s:String,age:Int) = {
println("name is "+s+",age = "+age)
}
def main(args: Array[String]): Unit = {
Lesson_ObjectWithParam("zhangsang")
Lesson_ObjectWithParam("lisi",18)
}
}
4)伴生类和伴生对象
class Person(xname :String , xage :Int){
var name = Person.name
val age = xage
var gender = "m"
def this(name:String,age:Int,g:String){
this(name,age)
gender = g
}
def sayName() = {
"my name is "+ name
}
}
object Person {
val name = "zhangsanfeng"
def main(args: Array[String]): Unit = {
val person = new Person("wagnwu",10,"f")
println(person.age);
println(person.sayName())
println(person.gender)
}
}
2.3 注意问题
- 建议类名首字母大写 ,方法首字母小写,类和方法命名建议符合驼峰命名法。
- scala 中的object是单例对象,相当于java中的工具类,可以看成是定义静态的方法的类。
- object不可以传参数。另:Trait不可以传参数
- scala中的class类默认可以传参数,默认的传参数就是默认的构造函数。
重写构造函数的时候,必须要调用默认的构造函数。
- class 类属性自带getter ,setter方法。
- 使用object时,不用new,使用class时要new ,并且new的时候,class中除了方法不执行,其他都执行。
- 如果在同一个文件中,object对象和class类的名称相同,则这个对象就是这个类的伴生对象,这个类就是这个对象的伴生类。可以互相访问私有变量。[
2.4 循环语句
1)if else
/**
* if else
*/
val age =18
if (age < 18 ){
println("no allow")
}else if (18<=age&&age<=20){
println("allow with other")
}else{
println("allow self")
}
2)for ,while,do…while
to和until 的用法(不带步长,带步长区别)
1 to 10 返回1到10的Range数组,包含10
* 1 until 10 返回1到10 Range数组 ,不包含10
for(i <- 1 to 10){
println(i)
}
9 9 乘法表
for(i <- 1 until 10){
for(j <- 1 until 10){
if(i>=j){
print(i + " * "+j + " = "+ i*j+"\t")
}
}
println()
}
for 里面加条件
for( i <- 1 to 1000;if(i>500);if(i%2==0)){
println(i)
}
val res = for(i <- 1 to 100 if(i>50) if(i%2==0)) yield i
print(res)
var i=0
while(i<20){
print(s"第 $i 次求婚")
i+=1;
}
for循环
//可以分号隔开,写入多个list赋值的变量,构成多层for循环
//scala中 不能写count++ count-- 只能写count+
for循环中可以加条件判断,可以使用分号隔开,也可以不使用分号
//可以在for循环中加入条件判断
for(i<- 1 to 10 ;if (i%2) == 0 ;if (i == 4) ){
println(i)
}
- scala中不能使用count++,count—只能使用count = count+1 ,count += 1
- for循环用yield 关键字返回一个集合
- while循环,while(){},do {}while()
//将for中的符合条件的元素通过yield关键字返回成一个集合
val list = for(i <- 1 to 10 ; if(i > 5 )) yield i
for( w <- list ){
println(w)
}
/**
* while 循环
*/
var index = 0
while(index < 100 ){
println("第"+index+"次while 循环")
index += 1
}
index = 0
do{
index +=1
println("第"+index+"次do while 循环")
}while(index <100 )
三.Scala方法与函数
- Scala方法的定义
- 有参方法
- 无参方法
def fun (a: Int , b: Int ) : Unit = {
println(a+b)
}
fun(1,1)
def fun1 (a : Int , b : Int)= a+b
println(fun1(1,2))
- 方法定义语法 用def来定义
- 可以定义传入的参数,要指定传入参数的类型
- 方法可以写返回值的类型也可以不写,会自动推断,有时候不能省略,必须写,比如在递归方法中或者方法的返回值是函数类型的时候。
- scala中方法有返回值时,可以写return,也可以不写return,会把方法中最后一行当做结果返回。当写return时,必须要写方法的返回值。
- 如果返回值可以一行搞定,可以将{}省略不写
- 传递给方法的参数可以在方法中使用,并且scala规定方法的传过来的参数为val的,不是var的。
- 如果去掉方法体前面的等号,那么这个方法返回类型必定是Unit的。这种说法无论方法体里面什么逻辑都成立,
- scala可以把任意类型转换为Unit.假设,里面的逻辑最后返回了一个string,那么这个返回值会被转换成Unit,并且值会被丢弃。
3.2递归方法
/**
* 递归方法
* 5的阶乘
*/
def fun2(num :Int) :Int= {
if(num ==1)
num
else
num * fun2(num-1)
}
print(fun2(5))
3.3 有默认值的方法
默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值。
如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称。
/**
* 包含默认参数值的函数
* 注意:
* 1.默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值
* 2.如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称
*/
def fun3(a :Int = 10,b:Int) = {
println(a+b)
}
fun3(b=2)
3.4 可变参数的方法
- 多个参数用逗号分开
/**
* 可变参数个数的函数
* 注意:多个参数逗号分开
*/
def fun4(elements :Int*)={
var sum = 0;
for(elem <- elements){
sum += elem
}
sum
}
println(fun4(1,2,3,4))
3.5匿名函数
有参匿名函数
无参匿名函数
有返回值的匿名函数
可以将匿名函数返回给val定义的值
匿名函数不能显式声明函数的返回类型
/**
* 匿名函数
* 1.有参数匿名函数
* 2.无参数匿名函数
* 3.有返回值的匿名函数
* 注意:
* 可以将匿名函数返回给定义的一个变量
*/
//有参数匿名函数
val value1 = (a : Int) => {
println(a)
}
value1(1)
//无参数匿名函数
val value2 = ()=>{
println("我爱尚学堂")
}
value2()
//有返回值的匿名函数
val value3 = (a:Int,b:Int) =>{
a+b
}
println(value3(4,4))
3.6 嵌套函数
/**
* 嵌套方法
* 例如:嵌套方法求5的阶乘
*/
def fun5(num:Int)={
def fun6(a:Int,b:Int):Int={
if(a == 1){
b
}else{
fun6(a-1,a*b)
}
}
fun6(num,1)
}
println(fun5(5))
fun("hello","ye")
def fun(s:String*):Unit={
s.foreach(elem =>{
println(elem)
})
}
3.7偏应用函数
def showLog(date:Date,log:String):Unit={
println(s"date is $date , log is $log")
}
val date = new Date()
showLog(date,"aaaa")
showLog(date,"bbbb")
showLog(date,"cccc")
def fun4:String => Unit = showLog(date,_:String)
fun4("aaa")
fun4("bbb")
fun4("ccc")
偏应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。
3.8 高阶函数
有下面三种方式:
1)函数的参数是函数
2)函数的返回是函数
3)函数的参数和函数的返回是函数
// 高阶函数---方法的参数是函数
def fun(a:Int,b:Int):Int={
a+b
}
def fun1(f:(Int,Int)=>Int,s:String):String={
var i:Int = f(100,200)
i+"#"+s
}
var res:String = fun1(fun,"scala")
print(res)
用匿名函数实现高阶函数
def fun1(f:(Int,Int)=>Int,s:String):String={
var i:Int = f(100,200)
i+"#"+s
}
var res:String = fun1((a:Int,b:Int)=>{a*b}:Int,"scala")
print(res)
def fun (s:String):(String,String)=>String={ //返回函数类型
def fun1(s1:String,s2:String):String={
s1+"~"+s2+"#"+s
}
fun1
}
print(fun("a")("b","c"))
def fun(f:(Int,Int)=>Int):(String,String)=>String ={
val i = f(1,2)
def fun1(s1:String,s2:String):String={
s1+"@"+s2+"*"+i
}
fun1
}
print(fun((a,b)=>{a+b})("hello","wokkk"))
3.9 柯里化函数
/**
* 柯里化函数
*/
def fun7(a :Int,b:Int)(c:Int,d:Int) = {
a+b+c+d
}
println(fun7(1,2)(3,4))
四.字符串
String
StringBuilder 可变
string操作方法举例
- 比较:equals
- 比较忽略大小写:equalsIgnoreCase
- indexOf:如果字符串中有传入的assci码对应的值,返回下标
/**
* String && StringBuilder
*/
val str = "abcd"
val str1 = "ABCD"
println(str.indexOf(97))
println(str.indexOf("b"))
println(str==str1)
/**
* compareToIgnoreCase
*
* 如果参数字符串等于此字符串,则返回值 0;
* 如果此字符串小于字符串参数,则返回一个小于 0 的值;
* 如果此字符串大于字符串参数,则返回一个大于 0 的值。
*
*/
println(str.compareToIgnoreCase(str1))
val strBuilder = new StringBuilder
strBuilder.append("abc")
// strBuilder.+('d')
strBuilder+ 'd'
// strBuilder.++=("efg")
strBuilder++= "efg"
// strBuilder.+=('h')
strBuilder+= 'h'
strBuilder.append(1.0)
strBuilder.append(18f)
println(strBuilder)
五.集合
5.1 数组
1)创建数组
- new Array[Int](10)
赋值:arr(0) = xxx
Array[String](“s1”,”s2”,”s3”)
2)数组遍历
for
foreach
3)创建一维数组和二维数组
4)数组中方法举例
Array.concate:合并数组
Array.fill(5)(“bjsxt”):创建初始值的定长数组
创建两种方式:
/**
* 创建数组两种方式:
* 1.new Array[String](3)
* 2.直接Array
*/
//创建类型为Int 长度为3的数组
val arr1 = new Array[Int](3)
//创建String 类型的数组,直接赋值
val arr2 = Array[String]("s100","s200","s300")
//赋值
arr1(0) = 100
arr1(1) = 200
arr1(2) = 300
import scala.collection.mutable.ArrayBuffer
// Array
object test06 {
def main(args:Array[String]) :Unit = {
//使用 ArrayBuffer需要导入包
val arr2 = ArrayBuffer[String]("a","b","c")
val arr = ArrayBuffer[Int]( elems = 1,2,3)
arr. += (4)
arr.foreach(println)
arr. +=:(100)
arr.foreach(println)
arr.append(7,8,9)
println("&&&&&&&")
arr.foreach(println)
val s = "bjsxt"
val s1 = "BJSXT"
println(s.equals(s1))
println(s.equalsIgnoreCase(s1))
println(s.indexOf(2)) // 对应的ascii码
println(s.indexOf("b"))
数组创建方式1
val arr = Array[String](xs="a","b","c","d")
//使用foreach遍历
arr.foreach(println(_))
arr.foreach(println)
arr.foreach(s=>{println(s)})
//使用for遍历
for(elem <- arr){
println(elem)
}
//数组创建方式2
val arr1 = new Array[Int](3)//此时括号里面就不是列举元素了,而是元素的个数
arr1.foreach(println(_))
arr1(0)= 100
arr1(1)=400
arr1(2)= 500
arr1.foreach(println(_))
创建二维数组
val arr3 = new Array[Array[String]](3)
arr3(0)=Array("1","2","3")
arr3(1)=Array("4","5","6")
arr3(2)=Array("7","8","9")
for(i <- 0 until arr3.length){
for(j <- 0 until arr3(i).length){
print(arr3(i)(j)+" ")
}
println()
}
var arr = Array[String](xs = "a","b","c","d")
var arr1 = Array[String](xs = "e","f","g")
//两个数组重新组合为一个新数组
var arrays :Array[String] =Array.concat(arr,arr1)
arrays.foreach(println)
var array:Array[String] = Array.fill(5)("hello")
array.foreach(println)
}
}
可变长度数组
/**
* 可变长度数组的定义
*/
val arr = ArrayBuffer[String]("a","b","c")
arr.append("hello","scala")//添加多个元素
arr.+=("end")//在最后追加元素
arr.+=:("start")//在开头添加元素
arr.foreach(println)
5.2 list
1)创建list
val list = List(1,2,3,4)
Nil长度为0的list
2)list遍历
foreach ,for
3)list方法举例
- filter:过滤元素
- count:计算符合条件的元素个数
- map:对元素操作
- flatmap :压扁扁平,先map再flat
import scala.collection.mutable.ListBuffer
object list1 {
def main(args :Array[String]):Unit={
val list = List[String](xs = "hello scala","hello java","hello spark")
val result : List[Array[String]] = list.map(s => {
s.split(" ")
})
result.foreach(arr => {
println("新数组")
arr.foreach(println)
})
println("*************")
val result2: List[String] = list.flatMap(s =>{
s.split(" ")
})
result2.foreach(println)
val result3:List[String] = list.filter(s =>{
"hello scala".equals(s)
})
result3.foreach(println)
val i:Int = list.count(s =>{
s.length>5
})
println(i)
//val list4 = ListBuffer[Int](elems = 1,2,3)
//val list4 = ListBuffer[Int]( 1,2,3) //不能使用xs
val list4 = List[String]( "hello","www") //不能使用elems
list4.foreach(println)
}
}
4)可变长list
/**
* 可变长list
*/
val listBuffer: ListBuffer[Int] = ListBuffer[Int](1,2,3,4,5)
listBuffer.append(6,7,8,9)//追加元素
listBuffer.+=(10)//在后面追加元素
listBuffer.+=:(100)//在开头加入元素
listBuffer.foreach(println)
5.3 set
1)创建set
注意:set集合会自动去重
2) set遍历
foreach,for
3)set方法举例
- 交集:intersect ,&
- 差集: diff ,&~
- 子集:subsetOf
- 最大:max
- 最小:min
- 转成数组,toList
- 转成字符串:mkString(“~”)
set方法总结
import scala.collection.mutable.Set
object set01 {
def main(args:Array[String]):Unit={
//val set: Set[Int] = Set(1,2,3,4,4,5) // 或者
// val set = Set[Int](elems= 1,2,3,4,4,5)
// set.foreach(println)
// for(elem <- set){
// println(elem)
// }
//val set1 = Set[Int](xs= 1,2,3,4) //不能使用xs
val set1 = Set[Int](elems = 1,2,3,4)
val set2 = Set[Int](elems = 3,4,5,6)
//交集
val result1 : Set[Int] = set1.intersect(set2)
result1.foreach(println)
println("*********")
// 差集
val result2 : Set[Int] = set1.diff(set2)
result2.foreach(println)
val ints = set1.filter(s =>{
s>2
})
ints.foreach(println)
println("&&&&&&&")
val newset = set1.+=(100)
println("newset: "+newset)
println("set1: "+set1)
}
}
//创建
val set1 = Set(1,2,3,4,4)
val set2 = Set(1,2,5)
//遍历
//注意:set会自动去重
set1.foreach { println}
for(s <- set1){
println(s)
}
println("*******")
/**
* 方法举例
*/
//交集
val set3 = set1.intersect(set2)
set3.foreach{println}
val set4 = set1.&(set2)
set4.foreach{println}
println("*******")
//差集
set1.diff(set2).foreach { println }
set1.&~(set2).foreach { println }
//子集
set1.subsetOf(set2)
//最大值
println(set1.max)
//最小值
println(set1.min)
println("****")
//转成数组,list
set1.toArray.foreach{println}
println("****")
set1.toList.foreach{println}
//mkString
println(set1.mkString)
println(set1.mkString("\t"))
可变长set
/**
* 可变长Set
*/
import scala.collection.mutable.Set
val set = Set[Int](1,2,3,4,5)
set.add(100)
set.+=(200)
set.+=(1,210,300)
set.foreach(println)
5.4 map
1)map创建
Map(1 –>”bjsxt’)
Map((1,”bjsxt”))
注意:创建map时,相同的key被后面的相同的key顶替掉,只保留一个
2)获取map的值
- map.get(“1”).get
- map.get(100).getOrElse(“no value”):如果map中没有对应项,赋值为getOrElse传的值。
//获取值
println(map.get("1").get)
val result = map.get(8).getOrElse("no value")
println(result)
3)map的遍历
for,foreach
//map遍历
for(x <- map){
println("====key:"+x._1+",value:"+x._2)
}
map.foreach(f => {
println("key:"+ f._1+" ,value:"+f._2)
})
4)map的keys
- map.keys
//遍历key
val keyIterable = map.keys
keyIterable.foreach { key => {
println("key:"+key+", value:"+map.get(key).get)
} }
println("---------")
5)map的values
- map.values
//遍历value
val valueIterable = map.values
valueIterable.foreach { value => {
println("value: "+ value)
} }
6)map的合并
- ++ 例:map1.++(map2) --map1中加入map2
- ++: 例:map1.++:(map2) –map2中加入map1
注意:合并map会将map中的相同key的value替换
//合并map
val map1 = Map(
(1,"a"),
(2,"b"),
(3,"c")
)
val map2 = Map(
(1,"aa"),
(2,"bb"),
(2,90),
(4,22),
(4,"dd")
)
map1.++:(map2).foreach(println)
7)map中的方法举例
- filter:过滤,留下符合条件的记录
- count:统计符合条件的记录数
- contains:map中是否包含某个key
- exist:符合条件的记录存在不存在
/**
* map方法
*/
//count
val countResult = map.count(p => {
p._2.equals("shsxt")
})
println(countResult)
//filter
map.filter(_._2.equals("shsxt")).foreach(println)
//contains
println(map.contains(2))
//exist
println(map.exists(f =>{
f._2.equals("xasxt")
}))
8)可变map
/**
* 可变长Map
*/
import scala.collection.mutable.Map
val map = Map[String,Int]()
map.put("hello",100)
map.put("world",200)
map.foreach(println)
样例
object map01 {
def main(args: Array[String]) :Unit={
import scala.collection.mutable.Map
import scala.collection.mutable
val map = Map[String,Int]()
map.put("a",100)
map.put("b",200)
map.put("c",300)
map.foreach(println(_))
val res1 :mutable.Map[String,Int] = map.filter(ts =>{
val key = ts._1
val value = ts._2
value == 200 //过滤条件,用== ,不是=
})
res1.foreach(println)
println("****")
val res2 :Int = map.count(ts =>{
val key = ts._1
val value = ts._2
value == 200 //过滤条件,用== ,不是=
})
println(res2)
// val map1 = Map[String,Int](("a",1),("b",2),("c",3),("d",4))
// val map2 = Map[String,Int](("a",100),("b",2),("c",300),("e",500))
// val result1 : Map[String,Int] = map1.++:(map2) //相同的值没有被覆盖
// println(result1)
// val map = Map[String,Int]("a"->100,"b"->200,("c",300),("c",400))
// println(map)
// val tempname: Option[Int]= map.get("a")
// println(tempname)
// val tempname2: Option[Int]= map.get("aa")
// println(tempname2)
// val tempname3 = map.get("aa").getOrElse("no Value")
// println(tempname3)
// val keys:Iterable[String] = map.keys
// println(keys)
// keys.foreach(s =>{
// print("key: "+s)
// println(" value: "+map.get(s).get) //最后这个get是从some(100)中取出100
// })
//
// val values :Iterable[Int] = map.values
// values.foreach(println(_))
}
}
5.5 元组
1)元组定义
与列表一样,与列表不同的是元组可以包含不同类型的元素。元组的值是通过将单个的值包含在圆括号中构成的。
2)创建元组与取值
- val tuple = new Tuple(1) 可以使用new
- val tuple2 = Tuple(1,2) 可以不使用new,也可以直接写成val tuple3 =(1,2,3)
- 取值用”._XX” 可以获取元组中的值
注意:tuple最多支持22个参数
//创建,最多支持22个
val tuple = new Tuple1(1)
val tuple2 = Tuple2("zhangsan",2)
val tuple3 = Tuple3(1,2,3)
val tuple4 = (1,2,3,4)
val tuple18 = Tuple18(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18)
val tuple22 = new Tuple22(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)
//使用
println(tuple2._1 + "\t"+tuple2._2)
val t = Tuple2((1,2),("zhangsan","lisi"))
println(t._1._2)
3)元组的遍历
tuple.productIterator得到迭代器,进而遍历
//遍历
val tupleIterator = tuple22.productIterator
while(tupleIterator.hasNext){
println(tupleIterator.next())
}
swap方法(只有二元组有)
/**
* 方法
*/
//翻转,只针对二元组
println(tuple2.swap)
//toString
println(tuple3.toString())
样例
object tuple01 {
def main(args:Array[String]):Unit={
val tuple01: Tuple1[(String, Int, Boolean)] = Tuple1("ye", 22, false)
val tuple02: (Int, Int, Int, String) = (1, 2, 3, "yt")
println(tuple02._2)
val iter:Iterator[Any] = tuple02.productIterator
while(iter.hasNext){
println(iter.next())
}
}
}
六. trait 特性
1)概念理解
Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类可以继承多个Trait,从结果来看就是实现了多重继承。Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait。
- 举例:trait中带属性带方法实现
注意:
- 继承的多个trait中如果有同名的方法和属性,必须要在类中使用“override”重新定义。
- trait中不可以传参数
trait Listen{
def listen(name:String):Unit={
println(s"$name is listening")
}
}
trait Read {
def read(name: String): Unit = {
println(s"$name is reading")
}
}
//继承多个trait的时候,第一个用关键字extends,其余的用with
class Human() extends Read with Listen{
}
object trait01 {
def main(args:Array[String]):Unit={
val h = new Human()
h.read("zhangsan")
h.listen("lisi")
}
}
模式匹
trait IsEqu{
def isEqu(o:Any):Boolean //
def isNotEqu(o:Any):Boolean = !isEqu(o)
}
class Point(xx:Int,xy:Int) extends IsEqu{
val x = xx
val y = xy
override def isEqu(o: Any): Boolean = {
o.isInstanceOf[Point] && o.asInstanceOf[Point].x == this.x
}
}
object trait02 {
def main(args:Array[String]):Unit={
val p1 = new Point(xx =1,xy=2)
val p2 = new Point(xx =1,xy=3)
println(p1.isEqu(p2))
}
}
七.模式匹配
- Scala 提供了强大的模式匹配机制,应用也非常广泛。
概念理解:
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。
每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
- 代码及注意点
- 模式匹配不仅可以匹配值还可以匹配类型
- 从上到下顺序匹配,如果匹配到则不再往下匹配
- 都匹配不上时,会匹配到case _ ,相当于default
- match 的最外面的”{ }”可以去掉看成一个语句
object match01 {
def main(args:Array[String]):Unit={
val tp =(1,1.0,"abc","a",true)
val iter:Iterator[Any] = tp.productIterator
// iter.foreach(println(_))
// println("!!!!!!!!")
// iter.foreach(s=>{println(MatchTest(s))})
// println("@@@@@@@@@@@")
// // s只用了一次,所以可以用下划线表示
// iter.foreach(MatchTest(_))
// println("!##########")
// //参数只有一个
iter.foreach(MatchTest)
}
def MatchTest(o:Any):Unit={
o match{
case 1=>{println("value is 1")}
case i:Int=>{println(s"type is Int,value = $i")}
case d:Double=>{println(s"type is Double,value = $d")}
case s:String=>{
println(s"type is String,value=$s")
}
case 'c'=>{println("value is c")}
case _=>{
println("no matching")
}
}
}
}
object Lesson_Match {
def main(args: Array[String]): Unit = {
val tuple = Tuple6(1,2,3f,4,"abc",55d)
val tupleIterator = tuple.productIterator
while(tupleIterator.hasNext){
matchTest(tupleIterator.next())
}
}
/**
* 注意点:
* 1.模式匹配不仅可以匹配值,还可以匹配类型
* 2.模式匹配中,如果匹配到对应的类型或值,就不再继续往下匹配
* 3.模式匹配中,都匹配不上时,会匹配到 case _ ,相当于default
*/
def matchTest(x:Any) ={
x match {
case x:Int=> println("type is Int")
case 1 => println("result is 1")
case 2 => println("result is 2")
case 3=> println("result is 3")
case 4 => println("result is 4")
case x:String => println("type is String")
// case x :Double => println("type is Double")
case _ => println("no match")
}
}
}
如果一个方法中没有match 只有case,这个函数可以定义成PartialFunction偏函数。
偏函数定义时,不能使用括号传参,默认定义PartialFunction中传入一个值,匹配上了对应的case,返回一个值。偏函数
/**
* 一个函数中只有case 没有match ,可以定义成PartailFunction 偏函数
*/
object Lesson_PartialFunction {
def MyTest : PartialFunction[String,String] = {
case "scala" =>{"scala"}
case "hello"=>{"hello"}
case _=> {"no match ..."}
}
def main(args: Array[String]): Unit = {
println(MyTest("scala"))
}
}
八.样例类(case classes)
- 概念理解
使用了case关键字的类定义就是样例类(case classes),样例类是种特殊的类。
实现了类构造参数的getter方法(构造参数默认被声明为val),当构造参数是声明为var类型的,它将帮你实现setter和getter方法。
- 样例类默认帮你实现了toString,equals,copy和hashCode等方法。
- 样例类可以new, 也可以不用new
- 例子:结合模式匹配的代码
case class Person1(name:String,age:Int)
object Lesson_CaseClass {
def main(args: Array[String]): Unit = {
val p1 = new Person1("zhangsan",10)
val p2 = Person1("lisi",20)
val p3 = Person1("wangwu",30)
val list = List(p1,p2,p3)
list.foreach { x => {
x match {
case Person1("zhangsan",10) => println("zhangsan")
case Person1("lisi",20) => println("lisi")
case _ => println("no match")
}
} }
}
}
九.隐式转换
隐式转换是在Scala编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在作用范围内自动推导出来合适的类型。
- 隐式值与隐式参数
隐式值是指在定义参数时前面加上implicit。隐式参数是指在定义方法时,方法中的部分参数是由implicit修饰
【必须使用柯里化的方式,将隐式参数写在后面的括号中】。
隐式转换作用就是:当调用方法时,不必手动传入方法中的隐式参数,Scala会自动在作用域范围内寻找隐式值自动传入。
隐式值和隐式参数注意:
1). 同类型的参数的隐式值只能在作用域内出现一次,同一个作用域内不能定义多个类型一样的隐式值。
2). implicit 关键字必须放在隐式参数定义的开头
3). 一个方法只有一个参数是隐式转换参数时,那么可以直接定义implicit关键字修饰的参数,调用时直接创建类型不传入参数即可。
4). 一个方法如果有多个参数,要实现部分参数的隐式转换,必须使用柯里化这种方式,隐式关键字出现在后面,只能出现一次
object implicit02 {
def sayName(implicit name:String):Unit={
println(s"$name is a student")
}
def sayName2(age:Int)(implicit name:String):Unit={
println(s"$name is a student,age is $age")
}
def main(args:Array[String]):Unit={
implicit val name:String = "zhangsan"
//sayName
sayName2(11)("lilfijdsv")
}
}
隐式转换函数注意:隐式转换函数只与函数的参数类型和返回类型有关,与函数名称无关,
所以作用域内不能有相同的参数类型和返回类型的不同名称隐式转换函数。
- 隐式类
使用implicit关键字修饰的类就是隐式类。若一个变量A没有某些方法或者某些变量时,而这个变量A可以调用某些方法或者某些变量时,可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入A即可。
隐式类注意:
1).隐式类必须定义在类,包对象,伴生对象中。
2).隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。
十.Actor Model
- 概念理解
Actor Model是用来编写并行计算或分布式系统的高层次抽象(类似java中的Thread)让程序员不必为
多线程模式下共享锁而烦恼,被用在Erlang 语言上, 高可用性99.9999999 % 一年只有31ms 宕机
Actors将状态和行为封装在一个轻量的进程/线程中,但是不和其他Actors分享状态,
每个Actors有自己的世界观,当需要和其他Actors交互时,通过发送事件和消息,发送是异步的,
非堵塞的(fire-andforget),发送消息后不必等另外Actors回复,也不必暂停,每个Actors有自己的消息队列,
进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,可以建立性能很好的事件驱动系统。
Actor的特征:
- ActorModel是消息传递模型,基本特征就是消息传递
- 消息发送是异步的,非阻塞的
- 消息一旦发送成功,不能修改
- Actor之间传递时,自己决定决定去检查消息,而不是一直等待,是异步非阻塞的
什么是Akka
Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Java 和Scala 的 Actor 模型应用,底层实现就是Actor,Akka是一个开发库和运行环境,可以用于构建高并发、分布式、可容错、事件驱动的基于JVM的应用。使构建高并发的分布式应用更加容易。
spark1.6版本之前,spark分布式节点之间的消息传递使用的就是Akka,底层也就是actor实现的。1.6之后使用的netty传输。
- 例:Actor简单例子发送接收消息
import scala.actors.Actor
class myActor extends Actor{
def act(){
while(true){
receive {
case x:String => println("get String ="+ x)
case x:Int => println("get Int")
case _ => println("get default")
}
}
}
}
object Lesson_Actor {
def main(args: Array[String]): Unit = {
//创建actor的消息接收和传递
val actor =new myActor()
//启动
actor.start()
//发送消息写法
actor ! "i love you !"
}
}
- 例:Actor与Actor之间通信
case class Message(actor:Actor,msg:Any)
class Actor1 extends Actor{
def act(){
while(true){
receive{
case msg :Message => {
println("i sava msg! = "+ msg.msg)
msg.actor!"i love you too !"
}
case msg :String => println(msg)
case _ => println("default msg!")
}
}
}
}
class Actor2(actor :Actor) extends Actor{
actor ! Message(this,"i love you !")
def act(){
while(true){
receive{
case msg :String => {
if(msg.equals("i love you too !")){
println(msg)
actor! "could we have a date !"
}
}
case _ => println("default msg!")
}
}
}
}
object Lesson_Actor2 {
def main(args: Array[String]): Unit = {
val actor1 = new Actor1()
actor1.start()
val actor2 = new Actor2(actor1)
actor2.start()
}
}
十一:实际应用--统计词频
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.rdd.RDD.rddToPairRDDFunctions
object WordCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf(
conf.setMaster("local").setAppName("WC")
val sc = new SparkContext(conf)
val lines :RDD[String] = sc.textFile("./words.txt")
val word :RDD[String] = lines.flatMap{lines => {
lines.split(" ")
}}
val pairs : RDD[(String,Int)] = word.map{ x => (x,1) }
val result = pairs.reduceByKey{(a,b)=> {a+b}}
result.sortBy(_._2,false).foreach(println)
//简化写法
lines.flatMap { _.split(" ")}.map { (_,1)}.reduceByKey(_+_).foreach(println)
}
}
参考文献:
http://dblab.xmu.edu.cn/blog/929-2
https://www.bilibili.com/video/BV1oJ411m7z3?p=11