Scala介绍

  1. Scala是一种针对JVM 将面向函数和面向对象技术组合在一起的编程语言。Scala编程语言近来抓住了很多开发者的眼球。它看起来像是一种纯粹的面向对象编程语言,而又无缝地结合了命令式和函数式的编程风格。Scala融汇了许多前所未有的特性,让开发者能够很好的而同时又运行于JVM之上。随着大数据的日益发展,scala必定会成为必不可少的开发语言。

  2. Spark1.6版本中使用的是Scala2.10版本;Spark2.0版本以上使用是Scala2.11版本。

  3. Scala官网6个特征:

    1).Java和scala可以混编

    2).类型推测(自动推测类型)

    3).并发和分布式

    4).特质,特征(类似java中interfaces 和 abstract结合)

    5).模式匹配(类似java switch)

    6).高阶函数

在这里插入图片描述

一、Scala基础

1.1 数据类型

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.2 变量和常量的声明

  • 定义变量或者常量的时候,也可以写上返回的类型,一般省略,如:val a:Int = 10
  • 常量不可再赋值
 /**
 * 定义变量和常量
 * 变量:用 var 定义 ,可修改
 * 常量:用 val 定义,不可修改
 */ 
 var name = "zhangsan"
 println(name)
 name = "lisi"
 println(name)
 val gender = 'm'
 //gender = "m" //错误,不能给常量再赋值注意:scala有个原则就是极简原则,不用写的东西一概不写。
 
 /**
 * 定义变量有两种形式:一种是像上面那样用val修饰,另一种是var进行修饰
 * val 定义的变量不可变相当与java中的final
 */ 
 //用表达式进行赋值
 val x = 1
 val y = if(1 > 0) 1 else -1
 //混和表达式
 val a = if (x > 0) 1 else "jay"
 //需要注意的是Any是所有的父类,相当于java里的Object 
 //else 缺失的表达式
 val p = if (x > 5) 1
 

1.3 类和对象

  • Scala中的 object 与 class:
    Scala 中没有 static 关键字,对于一个class来说,所有的方法和成员变量在实例被 new 出来之前都是无法访问的,因此class文件中的main方法也就没什么用了,因为不能写 public static void main(…){…}。
    在scala中没有静态方法和静态字段,所以在scala中可以用object来实现这些功能,即 scala object 中所有成员变量和方法默认都是 static 的,所以可以直接访问main方法。直接用对象名调用的方法都是采用这种实现方式,例如Array.toString。
  • 1、Scala object相当于Java中的单例,object中定义的全是静态的,相当于java中的工具类。
  • 2、Scala中定义变量使用var,定义常量使用val,变量可变,常量不可变,变量和常量类型可以省略不写,会自动推断
  • 3、Scala中每行后面都会有分号自动推断机制,不用显式写出“;”
  • 4、建议在Scala中命名使用驼峰命名法
  • 5、Scala类中可以传参,传参一定要指定类型,有了参数就默认有了构造。类中的方法默认有getter和setter方法。
  • 6、类中重写构造时,构造中第一行必须先调用默认的构造,def this(…){this(…) …}
  • 7、Scala中当new class时,类中除了方法不执行(除了构造方法),其它都执行
  • 8、在同一个Scala文件中,class名称和object名称一样时,这个类叫做对象的伴生类,这个对象叫做这个类的伴生对象,它们之间可以互相访问私有变量
  • 9、object不可以传参。当创建一个object时,如果传入参数,那么会自动寻找object中的相应参数个数的apply方法
  • 创建类
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())
  }
}

  • 对象中的apply方法

object中不可以传参,当创建一个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)
  }
}

  • 伴生类和伴生对象
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)
  }
}

注意点:

  • 建议类名首字母大写 ,方法首字母小写,类和方法命名建议符合驼峰命名法。
  • scala 中的object是单例对象,相当于java中的工具类,可以看成是定义静态的方法的类。object不可以传参数。另:Trait不可以传参数
  • scala中的class类默认可以传参数,默认的传参数就是默认的构造函数。

重写构造函数的时候,必须要调用默认的构造函数。

  • class 类属性自带getter ,setter方法。
  • 使用object时,不用new,使用class时要new ,并且new的时候,class中除了方法不执行(不包括构造),其他都执行。
  • 如果在同一个文件中,object对象和class类的名称相同,则这个对象就是这个类的伴生对象,这个类就是这个对象的伴生类。可以互相访问私有变量。

1.4 This

https://blog.csdn.net/qq_39521554/article/details/81045826?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

1.5 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")
  }

1.6 for,while,do…while

1. to和until 的用法(不带步长,带步长区别)

/**
* to和until
* 例:
* 1 to 10 返回1到10的Range数组,包含10
* 1 until 10 返回1到10 Range数组 ,不包含10
*/
    
    println(1 to 10 )//打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    println(1.to(10))//与上面等价,打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    
    println(1 to (10 ,2))//步长为2,从1开始打印 ,1,3,5,7,9
    println(1.to(10, 2)) 
    
    println(1 until 10 ) //不包含最后一个数,打印 1,2,3,4,5,6,7,8,9
    println(1.until(10))//与上面等价
    
	println(1 until (10 ,3 ))//步长为2,从1开始打印,打印1,4,7

在scala中,Range代表的是一段整数的范围,官方有关range的api:http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Range

var r = 1 to 10;//赋值1到10的整数  
var r = 1.to(10);
for(i <- 0 to 10);
for(i <- 0 until 10);
var r = Range(1,10);
var r = Range(1,10,2);//1到10的整数,步长为2,步长不能为0,默认步长为1

	这些底层其实都是Range,Range(1,10,2):1是初始值,10是条件,2是步长,步长也可以为负值,递减。

until和Range是左闭右开,1是包含的,10是不包含。而to是左右都包含。

2. 创建for循环

 /**
 * for 循环 
 */
    for( i <- 1 to 10 ){
      println(i)
}

//for循环数组
val arr=Array(“a”,”b”,”c”)
for (i<-arr){
  println(i)
}

3. 创建多层for循环(高级for循环)

//可以分号隔开,写入多个list赋值的变量,构成多层for循环
    //scala中 不能写count++ count-- 只能写count+
    var count = 0;
    for(i <- 1 to 10; j <- 1 until 10){
      println("i="+ i +",	j="+j)
      count += 1
    }
    println(count);
    
    //例子: 打印小九九
    for(i <- 1 until 10 ;j <- 1 until 10){
      if(i>=j){
    	  print(i +" * " + j + " = "+ i*j+"	")
        
      }
      if(i==j ){
        println()
      }
    }

4. for循环中可以加条件判断,可以使用分号隔开,也可以不使用分号(使用空格)

//可以在for循环中加入条件判断
for(i <- 1 to 10; if(i%2) == 0; if(i == 4) ){
   println(i)
}

5. scala中不能使用count++,count只能使用count = count+1 ,count += 1

6. for循环用yield 关键字返回一个集合(把满足条件的i组成一个集合)

 val result = for(i <- 1 to 100  if(i>50) if(i%2==0)) yield  i
 println(result)

7. 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 
}
/**
* do{} while() 循环
*/
index = 0 
do{
    index +=1 
    println("第" + index + "次do while 循环")
}while(index <100 )

8.懒加载

Val lazyVal={println(“I am too lazy);1}
lazy val lazyVal={println(“I am too lazy);1}

二、Scala方法与函数

2.1 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))  

注意点:

  • 定义常量或变量使用val或var,定义方法使用def
  • 可以定义传入的参数。定义方法传入的参数一定要指定类型
  • 方法可以写返回值的类型也可以不写,会自动推断,有时候不能省略,必须写,比如在递归方法中或者方法的返回值是函数类型的时候
  • scala中方法有返回值时,可以写return,也可以不写return。如果方法体中没有return,默认将方法体中最后一行计算的结果当作返回值返回,此时方法体的返回值类型可以省略,会自动推断返回值的类型;当写return时,必须要写方法的返回值类型
  • 方法的方法体如果可以一行搞定,那么方法体的花括号"{}"可以省略
  • 传递给方法的参数可以在方法中使用,并且scala规定方法的传过来的参数为val的,不是var的
  • 如果定义方法时,省略了方法名称和方法体之间的 “=”,那么无论方法体最后一行计算的结果是什么,都会被丢弃,返回Unit。这种说法无论方法体里面什么逻辑都成立,scala可以把任意类型转换为Unit,里面的逻辑最后返回了一个string,那么这个返回值会被转换成Unit,并且值会被丢弃。

2.2 方法与函数

定义一个方法:

def method(a:Int,b:Int) = a*b   
val a =2
method(3,5)

定义一个函数:

Val f1 = (x:Int,y:Int) => x+y
f1 (1,2)

匿名函数:

(x:Int,y:Int) => x+y

在函数式编程语言中,函数是"头等公民",它可以像任何其他数据类型一样被传递和操作,函数可以在方法中传递。

2.3 递归方法

递归方法要显示地声明函数的返回值类型。

	/**
	* 递归方法 
	* 5的阶乘
	*/
    def fun2(num :Int) :Int= {
      if(num ==1)
        num
      else 
        num * fun2(num-1)
    }
    print(fun2(5))

2.4 参数有默认值的方法

  • 默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值。
  • 如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称。
 	/**
     * 包含默认参数值的函数
     * 注意:
     * 1.默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值
     * 2.如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称
     */
    def fun3(a :Int = 10,b:Int) = {
      println(a+b)
    }
    fun3(b=2)

2.5 可变参数的方法

  • 在方法的参数后加 *,如果输入的是多类型,则需要把参数类型换成Any
  • 多个参数用逗号分开
 	/**
     * 可变参数个数的函数
     * 注意:多个参数逗号分开
     */
    def fun4(elements :Int*)={
      var sum = 0;
      for(elem <- elements){
        sum += elem
      }
      sum
    }
    println(fun4(1,2,3,4))

2.6 匿名函数

"() => {}"就是匿名函数。多用于方法的参数是函数时,常用匿名函数
要想单独调用匿名函数,需要给匿名函数赋值给一个变量,变量前用def和val或var定义均可

  1. 有参匿名函数
  2. 无参匿名函数
  3. 有返回值的匿名函数
  • 可以将匿名函数返回给val定义的值
	 /**
     * 匿名函数
     * 1.有参数匿名函数
     * 2.无参数匿名函数
     * 3.有返回值的匿名函数
     * 注意:
     * 可以将匿名函数返回给定义的一个变量
     */
    //有参数匿名函数
    val value1: (Int)=>Unit = (a : Int) => {
      println(a)
    }
    value1(1)
    //无参数匿名函数
    val value2 = ()=>{
      println("我爱学习")
    }
    value2()
    //有返回值的匿名函数
    val value3 = (a:Int,b:Int) =>{
      a+b
    }
    println(value3(4,4)) 

2.7 嵌套方法

	 /**
     * 嵌套方法
     * 例如:嵌套方法求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))

2.8 偏应用函数

  • 某些情况下,方法中参数非常多,调用这个方法非常频繁,每次调用只有固定的某个参数变化,其它都不变,可以定义偏应用来实现。
  • 偏应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。
  • 将默认的参数写死,可变的参数用下划线表示并指定类型。
	/**
     * 偏应用函数
     */
    def log(date :Date, s :String)= {
      println("date is "+ date +",log is "+ s)
    }
    
    val date = new Date()
    log(date ,"log1")
    log(date ,"log2")
    log(date ,"log3")
    
    //想要调用log,以上变化的是第二个参数,可以用偏应用函数处理
    val logWithDate = log(date,_:String)
    logWithDate("log11")
    logWithDate("log22")
    logWithDate("log33")

2.9 高阶函数

高阶函数有三种情况:

  • 函数的参数是函数
  • 函数的返回是函数
  • 函数的参数和函数的返回均是函数
	/**
     * 高阶函数
     * 函数的参数是函数		或者函数的返回是函数		或者函数的参数和返回都是函数
     */
    
    //函数的参数是函数
    def hightFun(f : (Int,Int) =>Int, a:Int ) : Int = {
      f(a,100)
    }
    def f(v1 :Int,v2: Int):Int  = {
      v1+v2
    }
    
    println(hightFun(f, 1))
    
    //函数的返回是函数
    //1,2,3,4相加
    def hightFun2(a : Int,b:Int) : (Int,Int)=>Int = {
      def f2 (v1: Int,v2:Int) :Int = {
        v1+v2+a+b
      }
      f2
    }
    println(hightFun2(1,2)(3,4))
    
    //函数的参数是函数,函数的返回是函数
    def hightFun3(f : (Int ,Int) => Int) : (Int,Int) => Int = {
      f
    } 
    println(hightFun3(f)(100,200))
    println(hightFun3((a,b) => {a+b})(200,200))
    //以上这句话还可以写成这样
    //如果函数的参数在方法体中只使用了一次 那么可以写成_表示
    println(hightFun3(_+_)(200,200))

2.10 柯里化函数

  • 柯里化函数是高阶函数的简化。会在定义隐式转换中用到。
  • 定义:柯里化(Currying)指的是把原来接受多个参数的函数变换成接受一个参数的函数过程,并且返回接受余下的参数且返回结果为一个新函数的技术。
  • scala柯里化风格的使用可以简化主函数的复杂度,提高主函数的自闭性,提高功能上的可扩张性、灵活性。可以编写出更加抽象,功能化和高效的函数式代码。
//柯理化
object KLH {
  def main(args: Array[String]): Unit = {
    def klh(x:Int)(y:Int) = x*y
    val res = klh(3)(_)
    println(res(4))
  }
}
/**
* 柯里化函数
*/
def fun7(a :Int,b:Int)(c:Int,d:Int) = {
  a+b+c+d
}
println(fun7(1,2)(3,4))

2.11 闭包

  • 闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
  • 闭包通常来讲可以简单的认为是可以访问不在当前作用域范围内的一个函数
/**
* scala中的闭包
* 闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
*/
object ClosureDemo {
  def main(args: Array[String]): Unit = {
    //变量y不处于其有效作用域时,函数还能够对变量进行访问
    val y=20
    val add=(x:Int)=>{
      x+y
    }
    //在add中有两个变量:x和y。其中的一个x是函数的形式参数,
    //在add方法被调用时,x被赋予一个新的值。y不是形式参数,而是自由变量
    println(add(3))
  }
}

三、Scala 字符串String

1. String
2. StringBuilder 可变
3. 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)

String方法总结: https://blog.csdn.net/qq_40078490/article/details/122848659

四、集合

4.1 数组Array

1. 创建数组

  • new ArrayInt

    赋值:arr(0) = xxx

  • ArrayString

2. 数组遍历

  • for
  • foreach

4. 创建一维数组和二维数组
5. 数组中方法举例

  • Array.concate:合并数组
  • Array.fill(5)("baway"):创建初始值的定长数组

创建两种方式:

	/**
     * 创建数组两种方式:
     * 1.new Array[String](3)
     * 2.直接Array
     */
    
    //方式一:先创建类型为Int 长度为3的数组
    val arr1 = new Array[Int](3)
    //再赋值
    arr1(0) = 100
    arr1(1) = 200
    arr1(2) = 300
    //方式二:创建String 类型的数组,直接赋值
    val arr2 = Array[String]("s100","s200","s300")
    

遍历两种方式:

 	/**
     * 遍历两种方式
    */
    //用for循环遍历
    for(i <- arr1){
    	  println(i)
    }
    
    for(s <- arr2){
      println(s)
    }
    
    //用foreach遍历
    arr1.foreach(i => {
      println(i)
    })
    
    arr2.foreach { 
      x => println(x) 
    }

创建二维数组

 	/**
     * 创建二维数组和遍历
     */
     //二维数组的创建
    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循环遍历
    for(i <- 0 until arr3.length){
      for(j <- 0 until arr3(i).length){
        print(arr3(i)(j)+"	")
      }
      println()
    }
    
    //用双重for循环遍历
    var count = 0
    for(arr <- arr3 ;i <- arr){
      if(count%3 == 0){
        println()
      }
      print(i+"	")
      count +=1 
    }
    
    //用foreach遍历
    arr3.foreach { arr  => {
      arr.foreach { println }
    }}
    
    //二维数组创建的另一种方式
    val arr4 = Array[Array[Int]](Array(1,2,3),Array(4,5,6))

数组中的方法: https://blog.csdn.net/qq_40078490/article/details/122848767

6. 可变长数组

/**
* 可变长度数组的定义
*/

val arr = ArrayBuffer[String]("a","b","c")
arr.append("hello","scala")//添加多个元素
arr.+=("end")//在最后追加元素
arr.+=:("start")//在开头添加元素
arr.foreach(println)

4.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再flatten

在这里插入图片描述
List中的map函数。map是一对一的,每进去一个数据,返回一个数组,最终返回一个由多个数组组成的的集合。
List中的flatMap函数。flatmap是一对多的,每进去一个数据,返回多个数据,最终形成一个数组。在map的基础上,将map返回的多个数组扁平化为一个数组。

 	//创建
    val list = List(1,2,3,4,5)
    
    //遍历
    list.foreach { x => println(x)}
	//list.foreach {println}
	
    //filter:过滤,返回结果为true的元素的集合
    val list1  = list.filter { x => x>3 }
    list1.foreach { println}
    
    //count:返回计算结果为true的元素的个数
    val value = list1.count { x => x>3 }
    println(value)
    
    //map:map是一对一的,每进去一个数据,返回一个数组,最终返回一个由多个数组组成的的集合。
    val nameList = List(
    		"hello baway",
    		"hello xasxt",
    		"hello shsxt"
        )
    val mapResult:List[Array[String]] = nameList.map{ x => x.split(" ") }
    mapResult.foreach{println}    
    
    //flatmap:flatmap是一对多的,每进去一个数据,返回多个数据,最终形成一个数组。
    //在 map的基础上,将map返回的多个数组扁平化为一个数组
    val flatMapResult : List[String] = nameList.flatMap{ x => x.split(" ") }
    flatMapResult.foreach { println }

4. List方法总结: https://blog.csdn.net/qq_40078490/article/details/122848834

import scala.collection.mutable.ListBuffer

object List_test {
  def main(args: Array[String]): Unit = {
//    val list = List[String]("hello scala","hello java","hello spark","a","abc")
//    val result: List[Array[String]] = list.map(s=> s.split(" "))
//    result.foreach(x=>println(x.toBuffer))

//
//    val res = list.filter(s => {
//      "hello scala".equals(s)
//    })
//    res.foreach(println)
//
//    val i = list.count(s=>{s.length<4})
//    println(i)

//    list(0)="java"

//    val list = ListBuffer[Int](1,2,3)
//    list.append(4,5,6)
//    list.+=:(100)
//    list.foreach(println)
//1 创建不可变的列表
val list = List(1,2,3,3,5,6,7,7)
    println(list)
    //2 操作元素
    println("第一个元素"+list(0))
    println("第2个元素"+list(0))
    println("最后一个元素"+list(list.size-1))
    //3 创建集合的方法
    val list1 = 100 :: 99 :: list
    val list2= 100 :: 99 :: 88 :: Nil
    val list3 = list1 ::: list
    val list4: Seq[Any] = 100 :: 99 :: list :: Nil
    println(list1)
    println(list2)
    println(list3)
    println(list4)
  }
}

5. 可变长List

/**
* 可变长List
*/
val listBuffer: ListBuffer[Int] = ListBuffer[Int](1,2,3,4,5)
listBuffer.append(6,7,8,9)//在后面追加元素
listBuffer.+=(10)//在后面追加元素10
listBuffer.+=:(100)//在开头加入元素100
listBuffer.foreach(println)

4.3 Set

1. 创建Set

注意:set集合会自动去重

2. Set遍历

foreach,for

3. Set方法举例

  • 交集:intersect ,&
  • 差集:diff , &~
  • 子集:subsetOf
  • 最大:max
  • 最小:min
  • 转成数组:toList
  • 转成字符串:mkString("~")

4. Set方法总结

   //创建 
   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方法总结: https://blog.csdn.net/qq_40078490/article/details/122848882

5. 可变长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)

//若想在下面继续使用不可变长Set,手动加上immutable
val set3 = immutable.Set[Int](2,3,4,5)

//若想在下面使用可变长Set,手动加上mutable
val set4 = mutable.Set[Int](7,8,9)

4.4 Map

1. Map创建

  • 注意:不能 new Map
  • Map中的元素是key-value形式,可以用 key->value 表示,也可以用 (key,value)表示
  • Map的两种表示方式:
    Map(1 –> baway)
    Map((1,"baway"))

注意:创建Map时,Map会去重,相同的key被后面的相同的key顶替掉,只保留一个

val map = Map(
      "1" -> "baway",
      2 -> "shsxt",
      (3,"xasxt")
    )

2. 获取map的值

  • get("1").get
  • get(100).getOrElse("no value"):如果map中没有对应项,赋值为getOrElse传的值。
	//获取某个key对应的value值
    //返回的是Option类型,在没有值时使用None,在有值时用Some来包含这个值,都是是Option的一个子类
    val option: Option[Int] = map.get("a")
    println(option)
    //获取option里面具体的值,此时若没有对应的value,会报错
    val value: Int = map.get("a").get
    println(value)
    //获取option里面具体的值,此时若没有对应的value,会返回getOrElse中的值
    val value1: Any = map.get("aa").getOrElse("no-value")
    println(value1)

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. 遍历key

  • map.keys
	//遍历key
    val keyIterable = map.keys
    keyIterable.foreach { key => {
      println("key:"+key+", value:"+map.get(key).get)
    } }
    println("---------")

5. 遍历value

  • map.values
	//遍历value(注意:不能根据value值获取key值,因为一个value可能有多个key)
    val valueIterable = map.values
    valueIterable.foreach { value => {
      println("value: "+ value)
    } }

6. 合并map

  • ++ 例:map1.++(map2) //将map2添加到map1中,相同key会被map2覆盖
  • ++: 例:map1.++:(map2) //将map1添加到map2中,相同key会被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")
      
    }))

Map方法总结https://blog.csdn.net/qq_40078490/article/details/122848917

8. 可变长Map

	/**
	* 可变长Map
	*/
	import scala.collection.mutable.Map
	val map = Map[String,Int]()
	map.put("hello",100)
	map.put("world",200)
	map.foreach(println)

4.5 元组Tuple

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. 元组的遍历

注意:元组的遍历不能直接用for循环或者foreach,必须先调用tuple.productIterator得到迭代器,进而遍历。

	//遍历方式1:
    val tupleIterator = tuple22.productIterator
    while(tupleIterator.hasNext){
      println(tupleIterator.next())
    }
    //遍历方式2:
	val iter: Iterator[Any] = tuple22.productIterator
    iter.foreach(println)

4. swap,toString方法

注意:swap元素翻转,只针对二元组

	/**
     * 方法
    */
    //翻转,只针对二元组
    println(tuple2.swap)
    
    //toString
    println(tuple3.toString())

4.6 Trait 特性

1. 概念理解

  • Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。

  • 与接口不同的是,它还可以定义属性和方法的实现。

  • 一般情况下Scala的类可以继承多个Trait,从结果来看就是实现了多重继承。Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait。

  • 一个类继承多个trait时,第一个关键字使用extends,之后使用with。

  • Trait中既可以有方法体实现了的方法,也可以有方法体不实现的方法。

  • 类继承了Trait要实现Trait中没有实现的方法。

2. 举例:trait中带属性带方法实现

注意:

  • 继承的多个trait中如果有同名的方法和属性,必须要在类中使用"override"重新定义。
  • trait中不可以传参数
trait Read {
  val readType = "Read"
  val gender = "m"
  def read(name:String){
	println(name+" is reading")
  }
}

trait Listen {
  val listenType = "Listen"
  val gender = "m"
  def listen(name:String){
	println(name + " is listenning")
  }
}

class Person() extends Read with Listen{
  override val gender = "f"
}

object test {
  def main(args: Array[String]): Unit = {
    val person = new Person()
    person.read("zhangsan")
    person.listen("lisi")
    println(person.listenType)
    println(person.readType)
    println(person.gender)
    
  }
}

3. 举例:trait中带方法不实现

object Lesson_Trait2 {
  def main(args: Array[String]): Unit = {
    val p1 = new Point(1,2)
    val p2 = new Point(1,3)
    println(p1.isEqule(p2))
    println(p1.isNotEqule(p2))
  }
}

trait Equle{
  def isEqule(x:Any) :Boolean 
  def isNotEqule(x : Any)  = {
    !isEqule(x)
  }
}

class Point(x:Int, y:Int) extends Equle {
  val xx = x
  val yy = y

  def isEqule(p:Any) = {
    p.isInstanceOf[Point] && p.asInstanceOf[Point].xx==xx
  }
  
}

五、模式匹配match

1. 概念理解:

Scala 提供了强大的模式匹配机制,应用也非常广泛。

一个模式匹配包含了一系列备选项,每个都开始于关键字 case。

每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。

2. 代码及注意点

  • 模式匹配不仅可以匹配值还可以匹配类型
  • 从上到下顺序匹配,如果匹配到则不再往下匹配
  • 都匹配不上时,会匹配到case _ ,相当于default
  • match 的最外面的"{ }"可以去掉看成一个语句
  • 匹配过程中会有数值的转换,如 Double 类型的 1.0 会转换成 Int 类型的 1
object Lesson_Match {
  def main(args: Array[String]): Unit = {
    val tp = (1,1.0,1.2,"abc",'e',true)
    val iter: Iterator[Any] = tp.productIterator
    iter.foreach(MatchTest)
  }

  def MatchTest(o:Any) = {
    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 'e' => println("value is e")
      //case true => println("value is true")
      case _ => println("no match...")
    }
  }
}

六、偏函数

如果一个方法中没有match 只有case,这个函数可以定义成PartialFunction偏函数。偏函数定义时,不能使用括号传参,默认定义PartialFunction中传入一个值,匹配上了对应的case,返回一个值,只能匹配同种类型。

  • 模式匹配可以匹配多种类型的元素,而偏函数只能匹配一种类型的元素,相当于java中的switch…case…
  • 模式匹配中,只有case没有match,可以将其定义为偏函数
  • 偏函数,只能匹配一个类型的值,匹配上了返回某个值
  • 偏函数定义时,不能使用()传参,要使用PartialFunction中传入一个值
  • 函数格式为 def 函数名: 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)

1. 概念理解

使用了case关键字的类定义就是样例类(case classes),样例类是种特殊的类。实现了类构造参数的getter方法(构造参数默认被声明为val),当构造参数是声明为var类型的,它将帮你实现setter和getter方法。

  • 样例类默认帮你实现了toString,equals,copy和hashCode等方法。
  • 样例类可以new,也可以不用new
  • 当一个类被定义成为case类后,Scala会自动帮你创建一个伴生对象并帮你实现了apply, unapply,setter,getter 和 toString,equals,copy 和 hashCode 等方法

2. 例子:结合模式匹配的代码

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编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在作用范围内自动推导出来合适的类型。

8.1 隐式值与隐式参数

隐式值是指在定义参数时前面加上implicit。隐式参数是指在定义方法时,方法中的 部分参数 是由implicit修饰【必须使用柯里化的方式,将隐式参数写在后面的括号中】。隐式转换作用就是:当调用方法时,不必手动传入方法中的隐式参数,Scala会自动在作用域范围内寻找隐式值自动传入。

隐式值和隐式参数注意:

1). 同类型的参数的隐式值只能在作用域内出现一次,同一个作用域内不能定义多个类型一样的隐式值。

2). Implicit 关键字必须放在隐式参数定义的开头

3). 一个方法只有一个参数是隐式转换参数时,那么可以直接定义implicit关键字修饰的参数,调用时直接创建类型不传入参数即可。

4). 一个方法如果有多个参数,要实现部分参数的隐式转换,必须使用柯里化这种方式,隐式关键字implicit 出现在后面的括号中,只能出现一次

object Lesson_Implicit {
  //定义一个隐式参数
  def sayName(implicit name:String) = {
    println(s"The student name is $name")
  }

  //部分参数是隐式参数,将隐式参数放到后面的括号中
  def sayInfo(age:Int)(implicit name:String) = {
    println(s"The student name is $name, age is $age")
  }

  def main(args: Array[String]): Unit = {
    //定义一个隐式值
    implicit val name: String = "zhangsan"
    sayName("lisi")
    sayName
    sayInfo(18)("wangwu")
    sayInfo(18)
  }
}

8.2 隐式转换函数

隐式转换函数是使用关键字implicit修饰的方法。当Scala运行时,假设如果A类型变量调用了method()这个方法,发现A类型的变量没有method()方法,而B类型有此method()方法,会在作用域中寻找有没有隐式转换函数将A类型转换成B类型,如果有隐式转换函数,那么A类型就可以调用method()这个方法。

隐式转换函数注意:隐式转换函数只与函数的参数类型和返回类型有关,与函数名称无关,所以作用域内不能有相同的参数类型和返回类型的不同名称隐式转换函数。

class Animal(name:String){
  def canFly(): Unit ={
    println(s"$name can fly...")
  }
}

class Rabbit(xname:String){
  val name = xname
}

object Lesson_Implicit2 {
  //定义一个隐式转换函数。输入某个类,返回另一个类。将某个类转换成另一个类。某个类没有某个功能,通过隐式转换使某个类具有另一个类中的功能。
  implicit def rabbitToAnimal(r:Rabbit):Animal = {
    new Animal(r.name)
  }

  def main(args: Array[String]): Unit = {
    val rabbit: Rabbit = new Rabbit("rabbit")
    //rabbit对象中没有canFly方法,会在作用域中寻找将Rabbit转化为Animal类的隐式转换方法,再调用Animal中的canFly方法。
    rabbit.canFly
  }
}

8.3 隐式类

使用 Implicit 关键字修饰的类就是隐式类。若一个变量A没有某些方法或者某些变量时,而这个变量A可以调用某些方法或者某些变量时,可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入A即可。

隐式类注意:

  • 隐式类必须定义在类,包对象,伴生对象中,即隐式类必须定义到class或object里面。

  • 隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。即同一个class或object中不能出现同类型构造的隐式类。

class Rabbit1(xname:String){
  val name = xname
}

object Lesson_Implicit3 {
  //定义一个隐式类。隐式类的构造只能有一个RABBIT1类型的参数,不能有其它参数
  implicit class Animal1(r:Rabbit1){
    def showName() = {
      println(s"${r.name} is a RABBIT")
    }
  }

  def main(args: Array[String]): Unit = {
    val rabbit: Rabbit1 = new Rabbit1("rabbit")
    //rabbit对象中没有showName方法,会在作用域中寻找将rabbit当作参数传进去的某种隐式类,看其有没有showName方法,有则调用。
    rabbit.showName()
  }
}

九、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传输。

例1: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 !"

  }
}

例2: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()
  }
}

十、WordCount

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)
    
  }
}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐