Scala知识笔记1

Scala知识笔记1

1 认识Scala

1.什么是Scala

Scala是一门现代的多范式语言,志在以简洁、优雅及类型安全的方式来表达常用的编程模型。它平滑地集成了面向对象和函数式语言的特性。

2.Scala是面向对象编程语言

鉴于一切值都是对象,可以说Scala是一门纯面向对象的语言。对象的类型和行为是由类和特质来描述的。类可以由子类化和一种灵活的、基于mixin的组合机制(它可作为多重继承的简单替代方案)来扩展。

3.Scala是函数式编程语言

鉴于一切函数都是值,又可以说Scala是一门函数式语言。Scala为定义匿名函数提供了轻量级的语法,支持高阶函数,允许函数嵌套及柯里化。Scala的样例类和内置支持的模式匹配代数模型在许多函数式编程语言中都被使用。对于那些并非类的成员函数,单例对象提供了便捷的方式去组织它们。

此外,通过对提取器的一般扩展,Scala的模式匹配概念使用了right-ignoring序列模式,自然地延伸到XML数据的处理。其中,for表达式对于构建查询很有用。这些特性使得Scala成为开发web服务等程序的理想选择。

4.Scala是静态类型的

Scala配备了一个拥有强大表达能力的类型系统,它可以静态地强制以安全、一致的方式使用抽象。典型来说,这个类型系统支持:
泛型类
型变注解
上、下 类型边界
作为对象成员的内部类和抽象类型
复合类型
显式类型的自我引用
隐式参数和隐式转化
多态方法
类型推断让用户不需要标明额外的类型信息。这些特性结合起来为安全可重用的编程抽象以及类型安全的扩展提供了强大的基础。

5.Scala是可扩展的

在实践中,特定领域应用的发展往往需要特定领域的语言扩展。Scala提供了一种语言机制的独特组合方式,使得可以方便地以库的形式添加新的语言结构。
很多场景下,这些扩展可以不通过类似宏(macros)的元编程工具完成。例如:
1.隐式类允许给已有的类型添加扩展方法。
2.字符串插值可以让用户使用自定义的插值器进行扩展。

6.Scala互操作

Scala设计的目标是与流行的Java运行环境(JRE)进行良好的互操作,特别是与主流的面向对象编程语言——Java的互操作尽可能的平滑。Java的最新特性如函数接口(SAMs)、lambda表达式、注解及泛型类 在Scala中都有类似的实现。
另外有些Java中并没有的特性,如缺省参数值和带名字的参数等,也是尽可能地向Java靠拢。Scala拥有类似Java的编译模型(独立编译、动态类加载),且允许使用已有的成千上万的高质量类库。

2 Scala基础语法(一)

1.变量 val/var

eg: val/var 变量名:类型=值

1.1 val 不可变的

var 可变的

1.2 声明与赋值同时定义

只能对var赋默认值,参数类型必须给定,值为 _

eg: var str:String=_

2.方法 def

2.1 格式:

[修饰符] def methodName(name1:ArgsType1,name2:ArgsType2)[:retrunType]={
     //方法体
  }

2.2 过程

方法的返回类型为Unit时,称之为过程(不建议大家这样做)

eg:
    def test1(a:Int):Unit={} 
      ----> 可以变成如下写法:
    def test1(a:Int){}

2.3 当方法为非递归方法时,可以将返回类型省略

def test(a:Int)={
      a
    }

eg: 如下为递归方法,不可以将返回类型省略。

    def fac(a:Int):Int={
      a*fac(a-1)
    }

2.4 方法调用 方法名(args)

1.对于无参数的方法,方法调用有两种: 方法名() 或者 方法名
2.方法调用时,可以通过形参进行无顺序传递。

    eg: def info(a:Int,b:Int,c:Int)={
          a+b+c
        }

方法调用:

      info(1,2,3)
      info(a=1,c=3,b=2)

注意:当使用形参传递参数时,如果有未指明形参名的必须与对应位置想匹配,不可以随意放置。

      info(b=2,a=1,6) //正确
      info(b=2,6,a=1) //错误 形参a,b有对应的值,那么为c赋值时,需要按照c在方法参数列表中的位置进行对应。 

2.5 可变参列表

1.只能位于参数列表的最后一个
2.表示形式: 类型*
3.可变参本质上是一个集合。

    def test(a:Int,b:String,c:Any*)={
      println(c)
    }

3.统一类型-了解数据类型

在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。

        Any (顶级父类,最顶级类)
  AnyVal    AnyRef(java.lang.Object)
    ^       List
    |       Option
  Double    YourClass
  Dloat       ^
  Long        |
  Int         |
  Short       |
  Byte        |
  Boolean     |
  Char        Null
  Unit        ^
    ^         |
    |         |
      Nothing(底部类型,所有类的子类,最底层类)
      
类层级结构:
          Any(顶级父类,最顶级类) 
  AnyVal        AnyRef(相当于Java中的Object)
8基本+Unit      引用类型
          Nothing(所有类的子类,最底层类)
类层级结构:
          Any(顶级父类,最顶级类) 
  AnyVal        AnyRef(相当于Java中的Object)
8基本+Unit      引用类型
          Nothing(所有类的子类,最底层类)

Any是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如equals、hashCode和toString。Any有两个直接子类:AnyVal和AnyRef。
AnyVal代表值类型。有9个预定义的非空的值类型分别是:Double、Float、Long、Int、Short、Byte、Char、Unit和Boolean。Unit是不带任何意义的值类型,它仅有一个实例可以像这样声明:()。所有的函数必须有返回,所以说有时候Unit也是有用的返回类型。
值类型的空间是平坦的;所有的值类都是scala.AnyVal的子类型,但是它们不是其他类的子类。但是不同的值类类型之间可以隐式地互相转换。例如,需要的时候,类scala.Int的实例可以通过隐式转换放宽到类scala.Long的实例;Int支持min、max、until、to、abs等操作,其实是从类Int隐式转换到scala.runtime.RichInt的。
AnyRef代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef的子类型。AnyRef其实是Java平台上java.lang.Object类的别名。因此Java里写的类和Scala里写的都继承自AnyRef。
如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object。
Scala类与Java类的不同在于它们还继承自一个名为ScalaObject的特别记号特质。是想要通过ScalaObject包含的Scala编译器定义和实现的方法让Scala程序的执行更高效。

类型转化.
下面是一个示例,说明了字符串、整型、布尔值和函数都是对象,这一点和其他对象一样:

  val list: List[Any] = List(
    "a string",
    732,  // an integer
    'c',  // a character
    true, // a boolean value
    () => "an anonymous function returning a string"
  )
  list.foreach(element => println(element))

Nothing和Null
scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些"边界情况"的特殊类型。
Nothing是所有类型的子类型,也称为底层类型。没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
Null是所有引用类型的子类型(即AnyRef的任意子类型)。它有一个单例值由关键字null所定义。Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null的替代方案。
例1:

  def error(message: String): Nothing = throw new RuntimeException(message)
  def divide(x: Int, y: Int): Int = if(y != 0) x / y else error("Can't divide by zero")

4.类 class

4.1 格式

class 类名 private[package] (主构造器参数列表) extends 类/特质 with 特质1 with 特质2{}	

eg:

 class Student(name:String,age:Int) extends Father with T1 with T2{
  //属性
  //方法
  //主构造器中的代码
  println(".....")
  def this()={
    //.......
  }
}

4.2 构造器

主构造器 : 与类的定义交织在一起
辅助构造器 :
1.通过方法名this重载构造器
2.辅助构造器的第一行代码必须是调用主构造器或者调用其他已经定义好的辅助构造器

4.3 主构造器参数列表

形式:

  1.参数名:类型  
  2.var 参数名:类型
  3.val 参数名:类型
  4.private val/var 参数名:类型
  5.@BeanProperty val/var 参数名:类型

总结:
1.当参数没有被修饰,且在类中没有使用该参数,该参数什么都不会生成。
2.当参数被val修饰时,会生成private修饰的字段/属性,以及提供一个名字为参数名的公有取值方法。
3.当参数被var修饰时,会生成private修饰的字段/属性,以及提供一个名字为参数名的公有取值方法和一个名字为参数名_=的公有赋值方法。
4.与2,3生成的字段以及方法个数一致,不同在于生成的方法时私有的。
5.@BeanProperty 会生成javaBean版本的set/get方法

5.对象 object 单例对象

格式: 

	object 对象名 extends 类/特质 with 特质1 with 特质2{}

总结:
类中的方法与字段都是非静态的。
对象中的方法与字段都是静态的。

程序入口

5.1Main方法
    object TestMain1{
      //程序入口
      def main(args:Array[String]):Unit={}
    }

将代码写到TestMain1.scala文件中。

5.2扩展App
    object TestMain2 extends App{
      //......程序入口
    }

将代码写到TestMain2.scala文件中。
准备工作:

	mkdir scala/day1
    vi TestMain1.scala

编译命令:

   scalac  TestMain1.scala
查看当前目录:

	 ls 

使用反编译工具打开 TestMain1.class

    scalap TestMain1
    javap -p TestMain1

运行命令:

    scala TestMain1

6.特质 trait

格式:

 trait 特质名 extends 类/特质 with 特质1 {}

总结:
1.可以包含具体方法/属性
2.可以包含抽象方法/属性
3.特质 可以 混入 类/对象/特质/实例(对象)

7.类型 type

格式:

	type 变量名=类型

总结:
1.当类型名称比较复杂时,可以对当前类型起个别名,方便后期使用。
2.当同一个源文件中出现两个不同包的同一类名时,可以通过类型别名来进行区分。

8.编译操作:

1.编译

1.1方式一scalac

使用Scala的基本编译器——scalac。开始编译源文件,但在编译完成之前会有几秒的停顿。因为每次编译器启动时,都要花一些时间扫描jar文件内容,并且在开始编译提交的源文件之前完成更多其他初始化工作(因此可能比Java程序编译慢)。命令如下:

	scalac -d bin/ Hello.scala
1.2方式二fsc

Scala的发布包里还包括了一个叫做fsc(快速Scala编译器,Fast Scala Compiler)的Scala编译器后台服务(daemon)。估计是针对Scala程序编译慢才刻意提供的服务。使用方法如下(其实用法和scalac差不多):

	fsc -d bin/ Hello.scala

第一次执行fsc时,会创建一个绑定在计算机端口上的本地服务器后台进程。然后它就会把文件列表通过端口发送给后台进程,由后台进程编译。下一次执行fsc时,检测到后台进程已经在运行了,于是fsc将只把文件列表发给后台进程,它会立刻开始编译文件。使用fsc,只须在首次运行的时候等待Java运行时环境的启动。如果想停止fsc后台进程,可以执行命令:
fsc -shutdownh

2.打包

打包过程和Java程序打包没什么两样,这里不做累述,仅给出命令行如下:

	jar -cvf hello.jar -C bin/ .

3.运行

3.1方式一java

使用java方式运行打包好的Scala程序有一个地方特别需要注意。这里,先看按Java程序的方式,不做任何处理运行Scala,命令行如下:

	java -cp hello.jar Hello 

报错提示Scala中的方法没有找到,为什么没有找到?这里先声明:该程序在Eclipse for Scala(IDE)上运行是没问题的,所以问题可能出在Scala的jar包没有引入,那么用-D参数引入jar包,命令行如下:

	java -Djava.ext.dirs=$SCALA_CP -cp hello.jar Hello 
	(注:SCALA_CP=$SCALA_HOME/lib)
3.2方式二scala

其实可以直接使用scala命令来运行的,不要以为scala命令只能开启Scala命令行模式,闲言少叙,直接看命令行:

	scala -cp hello.jar Hello 

4.总结

如果多次编译Scala程序建议使用fsc(每一种事物的存在都有它道理的),如果只须编译一次Scala程序,就用scalac吧,毕竟fsc用完记得关闭;
运行Scala程序建议使用scala命令,不建议用java命令。

5. 应用程序对象

5.1每个scala程序都必须从一个对象的main方法开始,这个方法的参数是Array[String]=>Unit
	object Hello{
	   def main(args: Array[String]): Unit = {
		println("hello , scala !")
	   }
	}
5.2除了提供自己的main方法之外,也可以扩展App特质,然后将程序代码放入构造方法体内:
	object Hello extends App{
	   println("Hello , hadoop ! ")
	}

如果需要命令行参数,则可以通过args属性得到:

	object Hello extends App{
	   if(args.length > 0)
		println("Hello , "+args(0))
	   else 
		println("hello , scala !")
	}

如果在调用该应用程序时设置了scala.time选项的话,程序退出时会显示逝去的时间。
eg:

		scalac Hello.scala
		scala -Dscala.time Hello briup

App特质扩展自另一个特质DelayedInit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedInit方法中.App特质的main方法捕获到命令行从那时,调用delayedInit方法,并且还可以根据要求打印出逝去的时间。

3 Scala基础语法(二)

3.1标识符

大体上与Java一致。

特殊点:
1.Scala标识符中不允许出现$
2.反引号`` 可以将关键字等一些特殊字符使用反引号引起来,这样就变成了一个合法的标识符。
eg:
return class val there is=“jthbh”

3.2关键字

Scala中的关键字基本上与Java一致。但存在个别Java关键字在Scala中不是关键字的特例。 eg:

 break

第一天学习过程中认识到的关键字:

class val var object def trait  type return this  super private protected Byte Short Int Long Char Float Double Boolean Unit Array  extends with abstract  

3.3操作符

算数运算符 逻辑运算符 位运算符 …等 与Java使用一致。
需要注意的是:
1.scala中本质上没有操作符,都是方法的调用。
2. Scala中 == 与 equals 一致,都是比较值
eq与ne比较的是地址。

  stu1.eq(stu2)
  1 + 2
  1.+(2)
  1 to 10  
  1.to(10)

3.在Scala中,运算符即是方法。 任何具有单个参数的方法都可以用作 中缀运算符。
例如,可以使用点号调用 +:

  	10.+(1)   	

而中缀运算符则更易读:

  	10 + 1

3.4注释

// 当行注释:
/* 多行注释: */ 
/**文档注释:*/

3.5控制结构(重点讲解)

5.1 块语句/表达式

val info={
  val b=0;
  val a=9;
  val sum=a+b;
  println(sum);
}

5.2 if表达式

val info=if(3>5){
  1.3
}else{
  0 
} 
val info=if(3<5){0.8}

5.3 输入输出

5.3.1.输出

1.输出通常使用print或println函数,后者在输出内容后追加一个换行符。
2.另外,还有一个带C风格格式化字符串的printf函数:
printf(“hello,%s! You are %d years old.\n”,“Fred”,42)
3.自2.10.0版本开始,Scala提供了一种新的机制来根据数据生成字符串:字符串插值。字符串插值允许使用者将变量引用直接插入处理过的字面字符中。如下例:

   val name="James"
   println(s"Hello,$name")//Hello,James

在上例中, s”Hello,$name” 是待处理字符串字面,编译器会对它做额外的工作。待处理字符串字面通过“号前的字符来标示(例如:上例中是s)。字符串插值的实现细节在 SIP-11 中有全面介绍。
3.1 用法:
Scala 提供了三种创新的字符串插值方法:s,f 和 raw.
1. s 字符串插值器
在任何字符串前加上s,就可以直接在串中使用变量了。你已经见过这个例子:

val name="James"
println(s"Hello,$name")//Hello,James 

此例中,$name嵌套在一个将被s字符串插值器处理的字符串中。插值器知道在这个字符串的这个地方应该插入这个name变量的值,以使输出字符串为Hello,James。使用s插值器,在这个字符串中可以使用任何在处理范围内的名字。
字符串插值器也可以处理任意的表达式。例如:

println(s"1+1=${1+1}") 

将会输出字符串1+1=2。任何表达式都可以嵌入到${}中。

val name="James"
println(s"Hello,$name")//Hello,James

在上例中, s”Hello,$name” 是待处理字符串字面,编译器会对它做额外的工作。待处理字符串字面通过“号前的字符来标示(例如:上例中是s)。字符串插值的实现细节在 SIP-11 中有全面介绍。
2. f 插值器
在任何字符串字面前加上 f,就可以生成简单的格式化串,功能相似于其他语言中的 printf 函数。当使用 f 插值器的时候,所有的变量引用都应当后跟一个printf-style格式的字符串,如%d。看下面这个例子:

val height=1.9d
val name="James"
println(f"$name%s is $height%2.2f meters tall")//James is 1.90 meters tall f

插值器是类型安全的。如果试图向只支持 int 的格式化串传入一个double 值,编译器则会报错。例如:

val height:Double=1.9d
scala>f"$height%4d"
<console>:9: error: type mismatch;
found : Double
required: Int
f"$height%4d"

^ f 插值器利用了java中的字符串数据格式。这种以%开头的格式在 [Formatter javadoc] 中有相关概述。如果在具体变量后没有%,则格式化程序默认使用 %s(串型)格式。
3. raw 插值器
除了对字面值中的字符不做编码外,raw 插值器与 s 插值器在功能上是相同的。如下是个被处理过的字符串:

scala>s"a\nb"
res0:String=
a
b 这里,s 插值器用回车代替了\n。而raw插值器却不会如此处理。
scala>raw"a\nb"
res1:String=a\nb 当不想输入\n被转换为回车的时候,raw 插值器是非常实用的。

除了以上三种字符串插值器外,使用者可以自定义插值器。(使用较少,如果有需求自行查文档了解)
5.3.2.输入
输入通常使用read函数,如readLine是从控制台读取一行输入,指定类型时为readT ,T是所要指定的类型,如readInt等
readLine带一个参数作为提示字符串
例如:

val name=readLine(“Your name :\n”) 
val age=readInt()

默认是scala.Predef._包下的,目前已经过时,
被scala.io.StdIn._包下的替代。
(注意:一个scala源码中默认导入3个包)

5.4 循环

while  do  for

for推导
1.格式: for(发生器){循环体}
eg:

      for(elem <- 1 to 10){...}

发生器的格式: i <- e
i 为变量
e 为需要遍历的元素(数组,列,表达式,数值范围等)
注意 for 表达式并不局限于使用列表。任何数据类型只要支持 withFilter,map,和 flatMap 操作(不同数据类型可能支持不同的操作)都可以用来做序列推导。
2.if守卫
格式:for(发生器 if守卫){循环体}
eg:

      for(elem <- 1 to 10 if i%2==0 if i%3==0){...}
      for(elem <- 1 to 10 if i%2==0&&i%3==0){...}

3.for嵌套
格式:for(发生器1;发生器2;…){…}
eg:

      for(i <- 1 to 9;j <- 1 to 9){....}

4.for嵌套+if守卫
格式:for(发生器1 if守卫;发生器2;… if守卫 if守卫 if守卫){…}
eg:

      for(i <- 1 to 10;j<- 1 to 9; if i%2==0 if j%3==0){...}
      for(i <- 1 to 10 if i%2==0;j<- 1 to 9 if j%3==0){...}

5.for yield 返回新的集合
格式:for(发生器) yield {循环体}
eg:

      val newCollection=for(i < 1 to 10) yield {i+2}

注意: 除了for yield之外的其他for循环都是一个过程,即没有返回值。

5.5 函数: 现阶段不具体区分方法和函数,因此与day1方法讲解的一致。

5.6 异常

Scala中的异常有三种处理办法:

1. try catch finally
    格式:
    
      try{}catch{case e:Exception=> }
      try{}catch{case e:Exception=>}finally{}
      try{}finally{}

注意:
1.支持上边这三种格式,注意最后一种在Java中是不支持的。
2.catch代码块中没有了所谓了参数列表。
3.catch代码块中使用case语句去匹配异常信息。
eg:

      try{
        //可能会发生异常的代码
      }catch{
        // e,x 只是一个变量,被val修饰的变量。因此,可以自己设置成符合Scala标识符规范的变量名即可。
        case e:IOException => 
          println(e.getMessage)
        case x:NullPointerException =>
          //...........
          //...........
      }
2. Option None Some (后期分享)
3. Try Success Failure (后期分享)

5.7 其他语句

scala不推荐使用return语句。
break/continue在C++中非常常见的控制结构语句,但在Scala中是不必要的,可以使用布尔值类型的量通过IF语句进行控制
注意:scala中没有break和contine关键字,如果想要结束多层循环,可以使用如下代码:

    import  scala.util.control.Breaks._
    break例子:
    breakable{
      for (i<- 1 to 10){
        if (i==5)
          break
      }
    }  
    continue例子:
    for(i<-0 until 10){
      breakable{
        if(i==3||i==6) {
          break
        }
        println(i)
      }
    }

3.6包与引用

import语句用于导入其他包中的成员(类,特质,函数等)

6.1包对象

每个包都可以有一个包对象,需要在父包中定义它,且名称与子包一样。
格式:

  package com.briup.test
  package object 包名{
    val defaultName="tom";
  }

在其他地方,这个常量可以用com.briup.test.包名.defaultName访问到。
包对象被编译成带有静态方法和字段的JVM类,名为包名.class,位于相应的包下。
对源文件使用相同的命名规则是好习惯,可以把包对象放到文件com/briup/test/包名/包名.scala。这样一来,任何想要对包增加函数或变量的话,都可以很容易地找到对应的包对象。

6.2默认导包

import java.lang._
import scala._
import scala.Predef._

注意:区分先后顺序

6.3包引入重命名

//全部导入
import scala.collection.mutable._ 
//重命名 type 
import java.util.{ HashMap => JavaHashMap } 
//将HashMap之外全部导入
import java.util.{ HashMap => _,_}
import java.util.{ HashMap,TreeSet}

6.4包的作用范围

与变量的作用范围一致,不可以在引用范围之外使用该包内的类,对象。

6.5如果存在命名冲突并且你需要从项目的根目录导入,请在包名称前加上 root

package accounts
import _root_.users._
import java.lang.String
import _root_.java.lang.String

6.6 Scala 不同于 Java 的一点是 Scala 可以在任何地方使用导入

def sqrtplus1(x: Int) = {
  import scala.math.sqrt
  sqrt(x) + 1.0
}

4 数组

存放一系列元素的容器

4.1分类

不可变数组 定长数组 Array
可变数组 缓冲数组 数组缓存 ArrayBuffer (集合,Seq)

	ArrayBuffer位于scala.collection.mutable包下。

4.2Array数组

4.2.1 定义方式

1.通过类构建
eg:

	val arr=new Array[T](size);

2.通过对象构建(统一对象构建原则)
eg:

	val arr2=Array("hello","scala","java");
	val arr3=Array.apply(1,2,3,4);
	val set=Set(1,2,34,5)

4.2.2 取值

	arr(index) 			

本质上调用的arr.apply(index)这个方法

	arr.take(num)
	arr.takeRight(num)
	arr.takeWhile(pf:T=>Boolean)

4.2.3 赋值

	arr(index)=值 
	arr.update(index,值)

4.2.4 遍历数组

	for(elem <- arr){...}
	for(index <- 0 until arr.length){...}
	arr.foreach(println)

4.2.5 获取数组的长度

	arr.length
	arr.size

4.3ArrayBuffer数组缓冲

4.3.1定义方式

1.通过类构建
eg:

	import scala.collection.mutable.ArrayBuffer
	val arrBuffer=new ArrayBuffer[T]();

2.通过对象构建(统一对象构建原则)
eg:

	  val aeeBuffer=ArrayBuffer(1,2,3)

4.3.2添加元素

	 +: ++ ++: +=: ++=: append appendAll insert insertAll 

需要注意的是:
1. 当方法名中有一个+号时,指的是添加 一个元素,返回一个新的集合/数组
2. 当方法名中有两个+号时,指的是添加 一个集合/数组容器,返回一个新的集合/数组
3. 当方法名中出现=号时,指的是会修改原集合。(只有可变集合才有包含=的方法)
4. 当方法名中没有=号时,不会修改原集合/数组,一般只会返回一个新的集合/数组

4.3.3移除元素

	 -  -- -= --= remove(index) remove(index,count) drop(count) dropRight(count) dropWhile(pf:T=>Boolean)

4.3.4常见方法

	take takeRight takeWhile  count 

算数集合:

	sum product max min  

排序:

	 sorted  按照集合类型默认排序规则进行排序(默认升序)
	 sortBy  按照自定义指定规则进行排序
	 sortWith自定义升序还是降序排列

遍历输出:

	foreach

转换:

	map filter
	val result=for(elem <- arr if elem %2==0)yield elem*2
	val newArr=arr.filter(_%2==0).map(_*2)
	val newArr=
	arr.filter(
		(x:Int) => {x%2==0}
	).map(
		(x:Int) => { x*2 }
	)

4.4多维数组: 数组的数组

	val arr2=new Array[Array[Int]](size);
	arr2(0)=Array(1,2,4)
	arr2(1)=Array(2,4,6,8)
	val arr3=Array.ofDim[Double](3,2,5,4)

5 元组

Tuple1 - Tuple22

5.1 若干个单个的值包含在圆括号便构成元组:

	eg:val g=(1 , 1.2,'A')  三元 元组   //(Int,Double,Char)类型的元组

5.2 映射是二元的元组,元组是不同类型的值的聚集

	(key,value) n=2 

5.3 利用方法_1、_2、_3访问元组的组元

	  val h=g._1 或 val h=g _1  3.+(2) 3 + 2		  

或者利用隐式的模式匹配

	  val (first,second,three)=(1,3.14,"Free");
	  val (first,second,_)=(1,3.14,"hhhhhhhhhhhh")
	  val (name,age,phone,address)=("tom",23,110,"南昌")

5.4 元组可以用于函数需要返回不止一个值得情况。

举例来说,StringOps的partition方法返回的是一对字符串,分别包含了满足某个条件和不满足条件的字符:

 	"New York".partition(_.isUpper)

5.5 注意区分下边两个的不同

	val x,y,z=(1,"hello",2)
	val (x,y,z)=(1,"hello",2)	

6映射

Map(Map集合中每个元素是一个二元元组)

6.1 二元元组的表示方法:

	(key,value) 或 key -> value

6.2 分为可变映射和不可变映射

	mutable.Map[K,V]
	immutable.Map[K,V]
	Map <==> Predef.Map <==> immutable.Map

注意:
scala.collection.Map 是immutable.Map和mutable.Map的超类
Scala优先采用不可变集合, scala.collection 包中的伴生对象产出不可变的集合

6.3 构建Map映射对象

	Map 是一个trait 和 object

因此构建方式只有:统一对象构建原则

	Map(elem1,elem2,elem3,...)
	<===>
	Map.apply(elem1,elem2,elem3,...)

6.4 构建一个空集合,可以使用empty方法

	import scala.collection.mutable;
	val mutableMap=mutable.Map.empty[K,V]		

注意:

	val map=muatble.Map();
	val map=mutable.Map[K,V]();

请自行测试这两个map集合的区别。(Nothing是所有类的子类,最底层类)

6.5 通过key获取value值

三种方式:

	map.apply(key)
	map.get(key)
	map.getOrElse(key,defaultValue)

6.6 Option(类似于集合的类)

当前对象中只包含0个或1个元素。
子类:

	Some(elem)  
	None

从Some中取值使用 get
注意:Option类是为了避免出现NullPointerException而设计。

6.7 添加元素

	+ ++ ++: (+= ++=) insert insertAll append appendAll

6.8 移除元素

	-  -- (-= --=) remove drop dropRight

6.9 遍历集合

eg:

	for(elem <- map){
		val key=elem._1
		val value=elem._2
	}

或:

	for( (key,value) <- map ){
		println(key+":"+value)
	}

只遍历key值

	map.keys
	map.keySet
	map.keysIterator

只遍历value值

	map.values
	map.valuesIterator

6.10 拉链操作

zip 将两个集合进行"等值连接"
zipAll 将两个集合进行"全连接",三个参数,第一个参数为连接的集合;第二个参数为原集合元素不足时的补位元素;第三个参数为连接集合元素不足时的补位元素;
zipWithIndex 将集合中的每个元素变成一个二元元组,二元元组的_2即位当前元素在集合中的索引。
unzip 将容器中的二元元组拆分,将每个二元元组中的_1放到一个集合中,_2的放到一个集合中。即拆分成两个集合。
unzip3 将容器中的三元元组拆分,将每个三元元组中的_1放到一个集合中,_2的放到一个集合中,_3的放到一个集合中。即拆分成了三个集合。

eg:

	val price=List(2,10,8)
	val num=List(10,10,10)

	val collection=price.zip(num)
	val newColl=for( (price,num) <- collection )yield{
			price*num
	}
	val totalSalary=newColl.sum;

或者

	val count=collection.map(x=> x._1*x._2).sum 

7 Ordered与Ordeing排序

7.1两个特质

7.1.1Scala提供两个特质(trait)

Ordered与Ordering用于比较。其中,Ordered混入(mix)Java的Comparable接口,而Ordering则混入Comparator接口。

		trait Ordered[A] extends Any with java.lang.Comparable[A] 
		trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable 

众所周知,
实现Comparable接口的类,其对象具有了可比较性;

		def compareTo(that: A): Int = compare(that)
		def compare(that: A): Int

实现Comparator接口的类,则提供一个外部比较器,用于比较两个对象。

		def compare(x: T, y: T): Int

7.1.2 Ordered与Ordering的区别与之相类似:

Ordered特质定义了相同类型间的比较方式,但这种内部比较方式是单一的;
Ordering则是提供比较器模板,可以自定义多种比较方式。

7.1.3 以下分析基于Scala2.11.8。

1.3.1 Ordered
Ordered特质更像是rich版的Comparable接口,除了compare方法外,更丰富了比较操作(<, >, <=, >=):

	trait Ordered[A] extends Any with java.lang.Comparable[A] {
		def compare(that: A): Int
		def <  (that: A): Boolean = (this compare that) <  0
		def >  (that: A): Boolean = (this compare that) >  0			
		def <= (that: A): Boolean = (this compare that) <= 0
		def >= (that: A): Boolean = (this compare that) >= 0
		def compareTo(that: A): Int = compare(that)
	}

此外,Ordered对象提供了从T到Ordered[T]的隐式转换(隐式参数为Ordering[T]):

 	object Ordered{
  		implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
  		new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
	}

1.3.2 Ordering
Ordering,内置函数Ordering.by与Ordering.on进行自定义排序:

	import scala.util.Sorting
	val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3))
	// sort by 2nd element
	Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2))
	// sort by the 3rd element, then 1st
	Sorting.quickSort(pairs)(Ordering[(Int, String)].on(x => (x._3, x._1)))

7.2 实战

7.2.1 比较

对于Person类,如何做让其对象具有可比较性呢?我们可使用Ordered对象的函数orderingToOrdered做隐式转换,但还需要组织一个Ordering[Person]的隐式参数:

	implicit object PersonOrdering extends Ordering[Person] {
	  override def compare(p1: Person, p2: Person): Int = {
	    p1.name == p2.name match {
	    	 case false => -p1.name.compareTo(p2.name)
			 case _ => p1.age - p2.age
	   }
	 }
	}
	val p1 = new Person("rain", 13)
	val p2 = new Person("rain", 14)
	import Ordered._
	p1 < p2 // True

7.2.2 Collection Sort

在实际项目中,我们常常需要对集合进行排序。回到开篇的问题——如何对Person类的集合做指定排序呢?下面用List集合作为demo,探讨在scala集合排序。首先,我们来看看List的sort函数:

	// scala.collection.SeqLike
	def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)
	def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)
	def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
		...
	}

1.若调用sorted函数做排序,则需要指定Ordering隐式参数:

	val p1 = new Person("rain", 24)
	val p2 = new Person("rain", 22)
	val p3 = new Person("Lily", 15)
	val list = List(p1, p2, p3)
	
	implicit object PersonOrdering extends Ordering[Person] {
		 override def compare(p1: Person, p2: Person): Int = {
		  p1.name == p2.name match {
	      case false => -p1.name.compareTo(p2.name)
	      case _ => p1.age - p2.age
	    }	
	   }
	}
	list.sorted 
	// res3: List[Person] = List(name: rain, age: 22, name: rain, age: 24, name: Lily, age: 15)

2.若使用sortWith,则需要定义返回值为Boolean的比较函数:

	list.sortWith { (p1: Person, p2: Person) =>
		p1.name == p2.name match {
			case false => -p1.name.compareTo(p2.name) < 0
		    case _ => p1.age - p2.age < 0
	   }
	}
	// res4: List[Person] = List(name: rain, age: 22, name: rain, age: 24, name: Lily, age: 15)

3.若使用sortBy,也需要指定Ordering隐式参数:

	implicit object PersonOrdering extends Ordering[Person] {
	  override def compare(p1: Person, p2: Person): Int = {
	    p1.name == p2.name match {
		     case false => -p1.name.compareTo(p2.name)
		     case _ => p1.age - p2.age
	    }
	  }
	}
	list.sortBy[Person](t => t)

7.2.3 RDD sort

1.RDD的sortBy函数,提供根据指定的key对RDD做全局的排序。sortBy定义如下:

	def sortBy[K](
		 f: (T) => K,
		ascending: Boolean = true,
	  numPartitions: Int = this.partitions.length)
	  (implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T] 

2.仅需定义key的隐式转换即可:

scala> val rdd = sc.parallelize(Array(new Person("rain", 24),
      new Person("rain", 22), new Person("Lily", 15)))

scala> implicit object PersonOrdering extends Ordering[Person] {
        override def compare(p1: Person, p2: Person): Int = {
	         p1.name == p2.name match {
	           case false => -p1.name.compareTo(p2.name)
	           case _ => p1.age - p2.age
          }
        }
      }
scala> rdd.sortBy[Person](t => t).collect()
// res1: Array[Person] = Array(name: rain, age: 22, name: rain, age: 24, name: Lily, age: 15)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值