一、Scala引入
1、学习Scala的目的
Spark、Kafka等相关大数据技术框架底层都是由Scala编程语言编写的,Spark我们自己编写分布式程序时,Spark提供了多种编程语言风格,但是我们比较常用的是使用Scala编程。
2、Scala的基本概念
- Scala是一门多范式编程语法,所谓多范式指的就是多种编程风格的语法,Scala主要是一门面向对象编程语言和函数式编程语言。
- Scala的发明人是马丁奥德斯基,Scala语言2001年左右诞生的,融合了Java和JS中很多特性。
- 同时Java中很多特性也是从Scala语言中吸收到,JDK8.0版本拉姆达表达式就是从Scala中吸收到
- JDK1.8版本的新特性
- Java中函数编程式接口、拉姆达表达式、方法引用、接口的组成、时间日期类、Stream API、Optional类(解决NullPointException的)。
- 函数式接口:只有一个抽象方法的接口称之为函数式接口,一般情况函数式接口需要使用@FunctionalInterface
- Java中的拉姆达(λ)表达式是和Java中的一个接口紧密相关的,接口函数式编程接口(接口中只有一个抽象方法)
语法:(形参列表)-> {方法体;}
拉姆达表达式的简化:
1、形参列表的类型可以省略的,因为拉姆达表达式对应的函数式编程接口的抽象方法是确定的。
2、如果参数列表只有一个 那么形参列表的()可以省略的。
3、如果方法体只有一行代码,而且这一行代码没有return关键字,那么方法体的{}可以省略,而且这一行代码不需要加分号 - Java中的方法引用相当于是拉姆达表达式的升级版本,主要就是用来替换整个拉姆达表达式的,当拉姆达表达式的方法体是引用了另外一个类的方法,并且方法体中没有多余的代码时,可以使用方法引用来简化代码。
- Scala的运行机制
- 编写xxx.scala源码文件,源码文件中可以使用Scala的SDK也可以使用Java的SDK
- 使用scalac编译xxx.scala源码文件成为Java的二进制字节码文件xxx.class
- 使用scala命令将xxx.class加载到Java的JVM虚拟机当中运行的
二、Scala环境搭建
1、安装步骤
Step1:安装并检测到JDK1.8版本或者其以上版本。
Step2:进入Scala的官网,点击自己想要的版本进行下载,我下载的是2.13.11,然后点击Windows对应的后缀为.msi的版本进行下载。
Step3:下载到本地后,双击 scala-2.13.11.msi
进行安装
因为scala占的内存不是很大,并且后续也不会自动变大所需要的存储空间,所以我就放在C盘不改变盘符了。
Step4:即可安装成功!打开命令行窗口输入命令scala -version
进行查看是否安装成功!
2、配置环境变量
Step1:我的电脑->右键->属性->高级系统设置->环境变量
Step2:用户变量栏目下新建->变量名为:SCALA_HOME,变量值为中scala安装的电脑路径
Step3:配置path变量->双击用户变量下的Path,进行如图所示的配置
Step4:一定要将所有的确定都点击完成后再关闭窗口。
3、测试Scala
打开电脑的命令行窗口,使用Scala的REPL(Read Eval Print Loop,交互式解释器)进行环境的搭建的基本测试。
前三个命令为:Scala的定义常量与变量的语法,后续会说到;
第四个命令为:将常量重新赋值时报错的场景;
第五个命令为:将数据类型为Int的变量b,重新赋值为数据类型为字符串的zs时报错的场景。
针对第五个命令报错的进一步说明:
- Scala属于强类型语言,变量类型一旦确定后,不可再进行修改。
- 强类型语言是一种强制类型定义的语言,一旦某一个变量被定义类型,如果不经过强制转换,则它永远就是该数据类型了。
- 弱类型语言是一种弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。我们可以举个弱类型语言的例子如下。
在电脑某文件夹下新建一个HW.scala文件,并进行内容的编写。然后我们使用命令行窗口进行编译和运行。
class HW{
}
object HW{
def main(args:Array[String]):Unit={
println("HW")
}
}
编译之后会生成两个新的文件
4、Scala与idea的集成
Step1:首先下载idea中提供的Scala的插件,安装并重启。
Step2:新建一个maven项目。
Step3:项目单机右键,选择Add Framework Support,将Scala的复选框勾选上,然后点击CREATE,再点击brower…选择Scala的安装路径。
Step4:新建一个名为scala的文件夹用于存放scala编写的代码,并将此文件夹设置为Sources Root
Step5:编写scala代码
5、关联源码
我们在刚刚下载Scala的官网下载对应的源码包scala-sources-2.13.11.tar.gz
,然后将压缩包进行解压到本地的稳定目录下。
6、 class 和 object 说明
Scala完全面向对象,故Scala去掉了Java中非面向对象的元素, 如static关键字, void类型
-
Scala无static关键字, 由object实现类似静态方法的功能(类名 .方法名);class关键字和Java中的class关键字作用相同,用来定义一个类。
-
class就是Scala中类,类中不能声明静态内容,所有的静态内容必须声明在该类的Object伴生对象中
-
对于无返回值的函数,Scala定义其返回值类型为Unit类。
三、常用语法、变量和数据类型
1、 注释
Scala 注释使用和 Java 完全一样。
单行注释://
多行注释:/* */
文档注释:/** */
2、变量和常量
常量:在程序执行的过程中,其值不会被改变的变量 。
-
Java 与Scala变量和常量语法对比
-
Java
- 变量类型 变量名称 = 初始值
- final 常量类型 常量名称 = 初始值
-
Scala
-
var 变量名 [: 变量类型] = 初始值
-
val 常量名 [: 常量类型] = 初始值
-
-
-
Scala声明变量的特点
-
声明变量时,类型可以省略,编译器自动推导,即类型推导。
-
类型确定后,就不能修改,说明 Scala 是强数据类型语言。
-
变量声明时,必须要有初始值 。
-
在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。
-
var 修饰的对象引用可以改变,val 修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)。
-
-
代码示例
-
class Demo01{ } object Demo01 { def main(args: Array[String]): Unit = { //声明变量 var a = 1 var b:Int = 1 a = 2 b = 2 println(a) println(b) //声明常量 val c = 1 //people1是var修饰的,people1的属性可以变,而且people1本身也可以变 var people1 = new People() people1.name = "ls" people1 = null //people2是val修饰的,那么people2本身就不可变(即people2的内存地址不能变),但是people2的属性是可以变,因为属性并没有用val修饰。 val people2 = new People() people2.name = "zs" people2 = null // 错误的,因为p2是val修饰的 } }
-
3 、标识符的命名规范
Scala 对各种变量、方法、函数等命名时使用的字符序列称为标识符。即:凡是自己可以起名字的地方都叫标识符。
-
命名规则——Scala 中的标识符声明,基本和 Java 是一致的,但是细节上会有所变化,有以下三种规则
-
Scala标识符由字母、数字、下划线、数学符号、$美元符号组成,其中数字不能开头。
-
如果标识符是数学符号,那么标识符中只能包含数学符号。
-
Scala的标识符可以是关键字和保留字,但是得使用*``*把关键字和保留字包起来即可。
Scala39个关键字: package, import, class, object, trait, extends, with, type, for,private, protected, abstract, sealed, final, implicit, lazy, override try, catch, finally, throw if, else, match, case, do, while, for, return, yield def, val, var this,super,new,true,false,null
-
-
代码示例
-
object Demo02 { def main(args: Array[String]): Unit = { var a1 = 1 var +-* = 2 println(+-*) var `var` = 3 println(`var`) } }
-
4、字符串输出
-
Scala的输出语言
-
使用Java的输出语句:System.out.xxxx
-
普通的输出语法:print println
-
模板输出语法:print/println(s"我有一个账号:$account")
-
格式输出语法:printf(可以使用一些特殊字符当作占位符,然后在方法后面传递参数取代占位符)
-
%d 十进制数字 %s 字符串 %c 字符 %e 指数浮点数 %f 浮点数 %i 整数(十进制) %o 八进制 %u 无符号十进制 %x 十六进制 %% 打印%
-
-
-
代码示例:
-
object Demo03 { def main(args: Array[String]): Unit = { print("zs") println("ls") var count = "123456" println(s"我有一个账号:$count") printf("我有一个账号%s",count) } }
-
5、键盘输入
在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。
直接无缝衔接使用Java的键盘输入 —— Scanner
使用Scala提供的自带的键盘输入 —— StdIn.readxxx()
-
基本语法
-
StdIn.readLine()
-
StdIn.readShort()
-
StdIn.readDouble()
-
-
代码示例
-
import scala.io.StdIn object Demo04 { def main(args: Array[String]): Unit = { //字符串 println("输入姓名:") var name = StdIn.readLine() //整形数字 println("输入年龄:") var age = StdIn.readInt() //浮点型数字 println("输入薪水:") var salary = StdIn.readDouble() //输出 println(s"name = $name" ) println(s"age = $age" ) println(s"salary = $salary") } }
-
6、数据类型(重点)
-
Java:基本数据类型:四类八种、引用数据类型。
-
Scala:没有基本数据类型一说,所有的类型都是对象,所有类型的顶尖父类是Any。
-
Any
-
AnyVal (数值类型)
- Byte Short Int Long Float Double Char Boolean
- Unit:void的完美的面向对象的替代品,Unit有一个唯一的值()
-
AnyRef(引用类型):Java中的所有类、Scala中所有类、Scala中所有集合、Null
-
-
-
Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)。
-
Scala中的StringOps是对Java中的String增强。
-
Unit:对应Java中的void ,用于方法返回值的位置,表示方法没有返回值。Unit是一个数据类型,只有一个对象就是() 。Void不是数据类型,只是一个关键字。
-
Null是一个类型,只有一个对象就是null 。它是所有引用类型(AnyRef)的子类,不能给值类型赋值为Null。
-
Nothing :是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。
-
代码示例
-
object Demo05 { def main(args: Array[String]): Unit = { var a:Int = 1 var d:Byte = a.toByte//强制类型转换 println(d) var b:Unit = () println(b) var c:String = null var e:AnyVal = d//类似于多态 println(e) } }
-
6.1 整数类型( Byte、 Short、 Int、 Long)
Scala 的整数类型就是用于存放整数值的,比如1,2,20等等。
- 整数型分类
数据类型 | 描述 |
---|---|
Byte [1] | 8 位有符号补码整数。数值区间为 -128 到 127 |
Short [2] | 16 位有符号补码整数。数值区间为 -32768 到 32767 |
Int [4] | 32 位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long[8] | 64 位有符号补码整数。数值区间为 -2^63 到2^(64-1)-1 |
-
整数型特点:
-
Scala 各整数类型有固定的表示范围和字段长度,不受具体操作的影响,以保证Scala 程序的可移植性。
-
Scala 的整型,默认为 Int 型,声明 Long 型,须后加‘l’或‘L’。
-
Scala 程序中变量常声明为 Int 型,除非不足以表示大数,才使用 Long。
-
6.2 浮点类型(Float、Double)
Scala 的浮点类型可以表示一个小数,比如1.0,20.2f等等。
- 浮点型分类
数据类型 | 描述 |
---|---|
Float [4] | 32 位, IEEE 754 标准的单精度浮点数 |
Double [8] | 64 位 IEEE 754 标准的双精度浮点数 |
- 特点
- Scala 的浮点型默认为 Double 型,声明 Float型,须后加‘f’或‘F’
6.3 字符类型(Char)
字符类型可以表示单个字符,字符类型是 Char。字符常量是用单引号 ’ ’ 括起来的单个字符。
-
字符类型中的一些特殊字符
-
\t :一个制表位
-
\n :换行符
-
\ \ :表示\
-
\ " :表示"
-
6.4 布尔类型(Boolean)
布尔类型也叫 Boolean 类型,Booolean 类型数据只允许取值 true 和 false,boolean 类型占 1 个字节。
6.5 Unit类型、Null类型和 Nothing 类型(重点)
数据类型 | 描述 |
---|---|
Unit | 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
Null | null , Null 类型只有一个实例值null |
Nothing | Nothing类型在Scala的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,可以用Nothing来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性) |
6.6 类型转换
- 数值类型自动转换
当 Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:
1、自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
2、把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
3、(byte,short)和 char 之间不会相互自动转换。
4、byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型。
注意:Scala 还提供了非常强大的隐式转换机制(隐式函数,隐式类等)
- 强制类型转换
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。
1、将数据由高精度转换为低精度,就需要使用到强制转换
2、强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
- 数值类型和 String 类型间转换
在程序开发中,我们经常需要将基本数值类型转成 String 类型。或者将 String 类型转成基本数值类型。
1、基本类型转 String 类型(语法:将基本类型的值+“” 即可)
2、String 类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、 s1.toLong、s1.toShort)
3、注意事项在将 String 类型转成基本数值类型时,要确保 String 类型能够转成有效的数据,比如我们可以把"123",转成一个整数,但是不能把"hello"转成一个整数。
-
代码示例
-
var a = "zs" + (1+1)
-
四、运算符
Scala 运算符的使用和 Java 运算符的使用基本相同,只有个别细节上不同。
1、算术运算符
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
+ | 正号 | +3 | 3 |
- | 负号 | b=4; -b | -4 |
+ | 加 | 5+5 | 10 |
- | 减 | 6-4 | 2 |
* | 乘 | 3*4 | 12 |
/ | 除 | 5/5 | 1 |
% | 取模(取余) | 7%5 | 2 |
+ | 字符串相加 | “He”+”llo” | “Hello” |
-
对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。
-
对一个数取模 a%b,和 Java 的取模规则一样。
2、关系运算符(比较运算符)
运算符 | 运算 | 范例 | 结果 |
---|---|---|---|
== | 相等于 | 4==3 | false |
!= | 不等于 | 4!=3 | true |
< | 小于 | 4<3 | false |
> | 大于 | 4>3 | true |
<= | 小于等于 | 4<=3 | false |
>= | 大于等于 | 4>=3 | true |
-
Java 和 Scala 中关于==的区别
-
Java:
-
==比较两个变量本身的值,即两个对象在内存中的首地址;
-
equals 比较字符串中所包含的内容是否相同。
-
-
Scala:
- ==更加类似于 Java 中的 equals,用于比较值相等,使用eq函数比较引用相等即地址相等。
-
3、逻辑运算符
基本语法用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个 Boolean 值。
运算符 | 描述 | 实例 A=true B=false |
---|---|---|
&& | 逻辑与 | (A && B) 运算结果为 false |
|| | 逻辑或 | (A || B) 运算结果为 true |
! | 逻辑非 | !(A && B) 运算结果为 true |
4、赋值运算符
赋值运算符就是将某个运算后的值,赋给指定的变量。
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋值给左边变量 | C = A + B 将 A + B 表达式结果赋值给 C |
+= | 相加后再赋值 | C += A 等于 C = C + A |
-= | 相减后再赋值 | C -= A 等于 C = C - A |
*= | 相乘后再赋值 | C *= A 等于 C = C * A |
/= | 相除后再赋值 | C /= A 等于 C = C / A |
%= | 求余后再赋值 | C %= A 等于 C = C % A |
<<= | 左移后赋值 | C <<= 2 等于 C = C << 2 |
>>= | 右移后赋值 | C >>= 2 等于 C = C >> 2 |
&= | 按位与后赋值 | C &= 2 等于 C = C & 2 |
^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |
|= | 按位或后赋值 | C |= 2 等于 C = C | 2 |
【注意】Scala 中没有++、–操作符,可以通过+=、-=来实现同样的效果;
5、位运算符
运算符 | 描述 |
---|---|
& | 按位与运算符 |
| | 按位或运算符 |
^ | 按位异或运算符 |
~ | 按位取反运算符 |
<< | 左移动运算符 |
>> | 右移动运算符 |
>>> | 无符号右移 |
6、Scala 运算符本质
Scala运算符本质上是一个函数,函数的名字是数学符号
-
当调用对象的方法时,点可以省略
-
如果函数参数只有一个,或者没有参数,()可以省略
-
代码示例:
-
object Demo06 { def k():Unit={ } def main(args: Array[String]): Unit = { val s = new String("zs") val s1 = "zs"; println(s ==s1) println(s.eq(s1)) var c: Int = 1 c.+=(1) c = c+1 println(c) //c++ val people = new People() people setName "zs" var result = k println(result) } }
-
7、元组
-
Scala中元组类型 —— Scala中一种比较特殊的数据类型,Scala类
-
元组也是一种集合,集合有个特点:最多可以存放22个元素
-
代码示例:
-
object Demo07 { def main(args: Array[String]): Unit = { val a:Tuple1[String] = new Tuple1[String]("zs") val b:Tuple2[String,Int] = new Tuple2[String,Int]("zs",2) } }
-
五、流程控制
-
Scala中流程控制:控制代码的执行逻辑的
-
顺序流程:代码从上而下依次执行
-
分支流程:根据判断的不同 走不同的分支路线
-
if:scala和Java的if分支是一样的
-
switch:Scala中没有提供switch语句,而是提供了一个功能更加强大的模式匹配来实现类似于switch的功能。
-
xxx match{
case 值或者表达式 => {分支}
case 值或者表达式 => {分支}
case _ =>{}
}
-
-
循环流程
-
1、分支控制 if-else
让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支
- 单分支
if (条件表达式) { 执行代码块 }
说明:当条件表达式为 ture 时,就会执行{ }的代码。
-
双分支
if (条件表达式) { 执行代码块 1 } else { 执行代码块 2 }
-
多分支
if (条件表达式 1) { 执行代码块 1 }else if (条件表达式 2) { 执行代码块 2 } …… else { 执行代码块 n }
-
嵌套分支
-
在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。 分支外面的分支结构称为外层分支。嵌套分支最好不要超过 3 层。
-
if(){ if(){ …. }else{ …. } }
-
2、Switch分支结构
在 Scala 中没有 Switch ,而是使用模式匹配来处理。
-
模式匹配
-
语法: x match{ case 值|x [模式守卫if] => case分支语句 case 值|x [模式守卫if] => case分支语句 case _ => case分支语句 }
-
【模式守卫】模式守卫可以做范围匹配,一般使用模式守卫时,case需要x
-
-
代码示例:
-
object Demo08 { def main(args: Array[String]): Unit = { var a:String = "spring" a match { case "spring" => println("春暖花开1") println("春暖花开2") case "summer" => println("夏日炎炎") case "autumn" => println("秋高气爽") case "winter" => println("银装素裹") case _ => println("输入的单词不是季节") } var b = 80 b match { case b if b>=0 && b<60 =>{ println("不及格") } case b if b>=60 && b<70 =>{ println("及格") } case b if b>=70 && b<80 =>{ println("中等") } case b if b>=80 && b<90 =>{ println("良") } case b if b>=90 && b<=100 =>{ println("优") } case _ => println("输入成绩有误!") } var c = "zsl" c match { case "zs" => println("匹配到了张三") case "ls" => println("匹配到了李四") case _ => println("啥也没匹配到") } } }
-
- Scala中循环流程:当满足某一个条件之后 不断重复执行某段代码块
- java : for、while、do while
- scala: for、while、do while
- 循环的四要素:循环变量、循环条件、循环体、循环的迭代
- Scala中while和do while和Java是没有任何的区别的,但是for循环和Java完全不一样的
- Scala中的for循环:
- for(变量 <- to/util类型的条件 循环守卫 循环步长;变量 <- to/util类型的条件 循环守卫 循环步长)
3、For 循环控制
Scala为for循环这一常见的控制结构提供了非常多的特性,这些 for循环的特性被称为for推导式或for表达式。
-
范围数据循环to
-
for(i <- start to end){ print(i + " ") }
-
i表示循环的变量
-
<-表示遍历右边的语法结构
-
to表示从start遍历到end 左闭右闭
-
-
-
范围数据循环Until
for(i <- start until end) { print(i + " ") }
- 取值返回左闭右开[start,end)
-
增强的for循环 —— 遍历集合或者数组
for(elem <- 集合/数组的变量)
-
循环守卫
for(i <- start to end if i != num) { print(i + " ") }
- 循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为 true 则进入循环体内部,为 false 则跳过,类似于 continue。等同于
for (i <- start to end){ if (i != num) { print(i + " ") } }
- 循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为 true 则进入循环体内部,为 false 则跳过,类似于 continue。等同于
-
循环步长
for (i <- start to end by num) { println("i=" + i) }
- by 表示步长,类似于i+=num
-
嵌套循环
for(i <- start to end; j <- start to end) { println(" i =" + i + " j = " + j) }
- 范围后一定要加;来隔断逻辑,等价于
for (i <- 1 to 3) { for (j <- 1 to 3) println("i ="+ i + " j=" + j) } }
- 范围后一定要加;来隔断逻辑,等价于
-
引入变量
-
for(i <- 1 to 3; j = 4 - i) { println("i=" + i + " j=" + j) }
-
for推导式一行中有多个表达式时,所以要加;隔断逻辑
-
for推导式有一个不成文的约定:当 for 推导式仅包含单一表达式时使用圆括号,当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下
for { i <- 1 to 3 j = 4 - i } { println("i=" + i + " j=" + j) }
-
-
-
循环返回值
-
val res = for(i <- 1 to 10) yield i println(res)
-
将遍历过程中处理的结果返回到一个新Vector集合中,使用 yield 关键字。
-
注意 :一般很少使用。
-
-
-
倒序打印
for(i <- 1 to 10 reverse){ println(i) }
- 如果想倒序打印一组数据,可以用reverse。
-
代码示例
-
object Demo09 { def main(args: Array[String]): Unit = { /** * until类型的for循环 */ for(i <- 1 until 10){ println(i) } /** * to类型的for循环 */ for(i <- 1 to 10){ println(i) } /** * 循环守卫 等同于判断条件,循环中,满足了某个条件之后才会执行循环体 */ for(i <- 1 to 100 if i % 2 == 0){ println(i) } /** * for循环的步长控制 * 如果循环守卫和循环步长同时出现 先写循环步长 再写循环守卫 */ for(i <- 1 to 100 by 3 if i % 3 ==0){ println(i) } /** * 嵌套循环 */ for(i <- 1 until 10;j <- 1 to i){ print(s"$i * $j = " + (i*j) + " "); if (i ==j){ println() } } /** * for循环的返回值 for循环不能有循环体的 * 将for循环的结果放到一个集合当中 */ var a = for(i <- 1 to 10) yield i println(a) /** * for 循环的值倒序输出 */ import scala.language.postfixOps for(i <- 1 to 10 reverse){ print(i + " ") } } }
-
4、while和do while循环
Scala中的while和do…while的使用和Java语言中用法相同
-
while循环
- 循环变量初始化 while (循环条件) { 循环体(语句) 循环变量迭代 }
- 循环条件是返回一个布尔值的表达式
- while 循环是先判断再执行语句
- 与 for 语句不同, while 语句没有返回值 ,即整个 while 语句的结果是 Unit 类型()
- 因为 while 中没有返回值 ,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在 while 循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用 for 循环。
- 循环变量初始化 while (循环条件) { 循环体(语句) 循环变量迭代 }
-
do while 循环控制
- 循环变量初始化; do{ 循环体(语句) 循环变量迭代 } while(循环条件)
- 循环条件是返回一个布尔值的表达式
- do while 循环是先执行,再判断
- 循环变量初始化; do{ 循环体(语句) 循环变量迭代 } while(循环条件)
5、循环终止
Scala内置控制结构特地去掉break和continue,是为了更好的适应函数式编程,推荐使用函数式的风格替代break和continue的功能而不是一个关键字。Scala中使用 breakable控制结构来实现break和 continue 功能。
6、多重循环
将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for ,while ,do while 均可以作为外层循环和内层循环。
【建议一般使用两层,最多不要超过 3 层】
六、函数式编程
1、函数基础
-
函数基本语法
-
def 函数名(参数列表):返回值类型={ 函数体 } Java中的方法的定义语法: 方法控制修饰符 [static|final|abstract] 返回值类型 方法名(参数列表) [thorws 异常]{ 方法体; }
-
-
函数和方法的区别
-
为完成某一功能的程序语句的集合,称为函数。
-
类中的函数称之方法。
-
Scala 语言可以在任何的语法结构中声明任何的语法。
-
函数没有重载和重写的概念;方法可以进行重载和重写。
-
Scala 中函数可以嵌套定义。
-
-
函数分类
- 无参,无返回值
- 无参,有返回值
- 有参,无返回值
- 有参,有返回值
- 多参,无返回值
- 多参,有返回值
class Demo10 {
/**
* 无参数 无返回值的函数
*/
def a():Unit={}
/**
* 无参数 有返回值的函数
*/
def b():Int={
return 1;
}
/**
* 有参数 有返回值的函数
*/
def c(a:Int): Int = {
return a;
}
/**
* 有参数 无返回值的函数
*/
def d(a: Int): Unit = {
}
/**
* 多参数 无返回值的函数
*/
def e(a: Int,b:Int): Unit = {
}
/**
* 多参数 有返回值的函数
*/
def f(a: Int, b: Int): Int = {
return a+b;
}
}
2、函数参数
-
可变长参数
- 如果参数列表中存在多个参数,那么可变参数一般放置在最后
-
参数默认值,一般将有默认值的参数放置在参数列表的后面
- 特点:如果参数带了默认值 那么传递的时候可以不用传递该参数值
-
带名参数
- 如果函数定义的时候 有一个参数没有默认值 而且位于参数的最后 可以使用参数名=参数值的形式传递数据
-
import java.io.IOException /** * 函数的第一个问题: 函数名:小驼峰 * 函数的第二个问题: 函数的参数问题 * 1、参数列表的声明语法: 参数名:参数类型,参数名:参数类型 * 2、可变长形参:参数名:数据类型* 必须位于参数列表的最后一个位置,而且一个方法只能有一个可变长形参 * 3、形参的默认值问题:scala中定义的函数参数可以带有默认值,如果一旦函数的参数带有默认值,那么调用函数时,带有默认值的参数就可以不用传递数据了 * 一般要求默认值形参位于形参列表的最后,如果位于第一位,那么不具备默认值的形参需要通过具名实参的方式传递参数 * 函数的第三个问题:函数的返回值问题 * 函数的返回值可以是任何一种数据类型 Unit代表无返回值的 Nothingd */ class Demo11 { def demo(studentName:AnyVal*):Unit={ /** * 增强的for循环 <- 直接跟上一个集合 */ for (elem <- studentName) { println(elem) } } /** * 形参的默认值 */ def write(line:String,append:Boolean = true):Unit={ println(line) } /** * 形参的默认值 */ def write1(append: Boolean = true,line: String): Unit = { println(line) } /** * 无正常返回值的函数 */ def test():Nothing={ throw new IOException(); } } object Demo11{ def main(args: Array[String]): Unit = { val demo11 = new Demo11(); demo11.demo(1,2,3) demo11.write("zs") demo11.write("zs",false) demo11.write1(line = "zs") val a = demo11.test(); } }
3、函数的高阶函数
-
【注】在Scala中,函数是一等公民,函数可以在Scala的任何位置充当任何的角色,函数可以声明在类中,也可以声明在函数中,还可以声明在参数列表中、还可以当作返回值,还可以当作一个变量
-
函数当变量使用:把函数重命名了
-
var d:函数的定义 = 函数名 _
-
【注意】函数的类型如何声明: (形参类型列表) => 返回值类型
-
示例: 函数的定义: def test(a:Int,b:Int):Int={a+b} 函数的类型写法:(Int,Int) => Int
-
package function /** * 函数的高阶使用 */ object Demo01 { def main(args: Array[String]): Unit = { /** * 1、函数充当变量使用 * test _ 将函数整体赋值给一个变量 */ def test(a:Int,b:Int):Int={a+b} def test1(a:String,b:Int){} /** * 此时a是一个函数的类型 * Scala中函数类型定义语法:(参数类型列表) => 函数的返回值类型 */ var a:(Int,Int) => Int = test _ var b:(String,Int) => Unit = test1 _ println(a(1,2)) println(test(1,2)) } }
-
-
函数当参数来使用
-
语法: def test(a:Int,f:(Int,Int)=>Int):Unit={ }
-
package function /** * 函数的高阶使用:函数当作参数来使用 */ object Demo02 { /** * 要求 输入三个参数,其中前两个参数要根据第三个参数提供的函数计算出一个Int类型的值 * * @param a * @param b * @param f */ def test(a:Int,b:Int,f:(Int,Int) => Int):Unit={ val result:Int = f(a, b) println(result) } def main(args: Array[String]): Unit = { /** * 想让test函数计算a和b的最大值 */ def max(a:Int,b:Int):Int={ if (a>b) a else b } test(1,2,max _)//2 test(1,2,(a:Int,b:Int)=>{a+b})//3 test(1,2,(a,b)=>{a+b})//3 test(1,2,(a,b)=>a+b)//3 test(1,2,_+_)//3 } }
-
-
函数当作返回值来使用
-
语法: def test():(Int,Int)=>Int={ }
-
package function /** * 函数的高阶使用--函数充当返回值 */ object Demo03 { def test(): (Int,Int)=>Int = { def demo(a:Int,b:Int):Int={ a+b } return demo _ return (a:Int,b:Int)=>{a+b} return (a,b)=>{a+b} return (a,b)=>a+b return _+_ } }
-
def test(a:Int,b:Int):Int = {
a+b
}
var f1:(Int,Int)=>Int = test _
var f2:(Int,Int)=>Int = (a:Int,b:Int) => {a+b} //使用匿名函数
var f3:(Int,Int)=>Int = _ + _
4、函数的高阶使用
-
函数的闭包问题
-
函数闭包指的是将不属于本函数的变量或者对象也包含进来,直到该函数运行完成,外部变量或者对象才可以被释放。
-
package function object Demo04 { def main(args: Array[String]): Unit = { var x:Int = 1 def test(a:Int):Int={ a*x } test(1) } }
-
-
函数的柯里化
-
将一个接收多个参数的函数给他转换成为一个接收单个参数的函数的过程
-
将一个接收多个参数的函数转换成为一个返回了函数的函数,返回的函数传递的值就是原先的第二个参数
-
其实是闭包的一个使用场景
-
package function /** * 函数的柯里化 * test(1,2) * test(1)(2) */ object Demo05 { def sum(a:Int,b:Int,c:Int):Int={ a+b+c } def sum1(a:Int):Int=>Int=>Int={ def sum2(b:Int):Int=>Int={ def sum3(c:Int):Int={ a+b+c } return sum3 _ } return sum2 _ } def test(a:Int,b:Int=1):Int={ a*b } /** * test1函数返回的是一个函数 函数的类型是Int=>Int * 返回的函数的输入参数是原先函数的第二个参数 * 返回的函数的输出是原先函数的计算结果类型 * @param a * @return */ def test1(a:Int):Int=>Int={ (b:Int)=>{a*b} } def main(args: Array[String]): Unit = { var r:Int = test(1,2) println(r) var r1:Int = test1(1)(2) println(r1) sum(1,2,3) var r2:Int = sum1(1)(2)(3) println(r2) } }
案例: def test1(a:Int,b:Int,c:Int,d:Int):Int={ a+b+c+d } def sum1(a:Int):Int =>Int =>Int => Int = { def sum2(b:Int):Int =>Int => Int = { def sum3(c:Int):Int =>Int = { def sum4(d:Int):Int = { a+b+c+d } sum4 _ } sum3 _ } sum2 _ } var sum:Int = sum1(1)(2)(3)(4) var sumT:Int = test1(1,2,3,4) println(sum + " " + sumT)/
-
-
递归函数
-
函数内部调用本函数
-
递归三要素
- 1、递归入口:自己调用自己的的逻辑
- 2、递归出口:不能调用自己的逻辑
- 3、递归条件必须向出口迭代
-
package function object Demo06 { def main(args: Array[String]): Unit = { /** * 定义一个函数 求∑100 */ var r = sum(1000000) println(r) } def sum(a:Int):Int={ if(a == 1){ 1 }else{ a+sum(a-1) } } }
-
-
函数调用的惰性加载
-
惰性加载指的是将函数的调用延迟到第一次使用函数的返回值的时候才会调用
-
使用语法: lazy val 变量名 = 函数名(实参)
此时函数的调用在第一次使用变量的时候才会调用
一旦惰性加载,变量名只能使用val修饰 -
package function object Demo07 { def sum(a:Int,b:Int):Int = { println("sum函数执行了") a+b } def main(args: Array[String]): Unit = { //函数的调用被惰性加载了 lazy val a = sum(1,2) println("main函数中sum函数调用之后的输出") println(a) } }
-
5、函数的简化操作
-
声明的简化
- 1、如果函数没有参数,那么函数的括号可以省略
def test:Unit={}
- 2、函数的返回值可以省略的,可以根据函数体的最后一行自动推断,【注意】如果函数体的最后一行使用return 关键字返回数据,那么函数的返回值一定不能省略的
def test = {
1
} - 2、函数的返回值可以省略的,可以根据函数体的最后一行自动推断,【注意】如果函数体的最后一行使用return 关键字返回数据,那么函数的返回值一定不能省略的
def test = {
1
} - 3、函数体中,函数的返回值前的return关键字可以省略的,自动根据最后一行推断函数的返回值
- 4、如果函数的返回值类型是Unit类型 那么=号和函数的返回值都可以省略
def test{} - 5、匿名函数
- 定义:定义函数时,只关注函数的逻辑,不关注函数的名字,此时我们就可以使用匿名函数来定义函数:
(形参列表) => {函数体} - 使用场景:当函数当作参数或者当作返回值使用的时候,可以使用匿名函数传递
- 简化
- 1、匿名函数的形参列表的类型可以省略的,因为当作参数或者返回值使用的时候,参数的类型定义死了
- 2、如果匿名函数的函数体只有一个,那么{}可以省略了
- 3、如果形参的参数名在函数体中只出现了一次,那么参数名就可以使用_替代,同时形参列表可以省略了
- 定义:定义函数时,只关注函数的逻辑,不关注函数的名字,此时我们就可以使用匿名函数来定义函数:
- 1、如果函数没有参数,那么函数的括号可以省略
-
调用的简化:一般不建议使用,建议运算符的函数调用简化
- 调用的语法:对象名|类名.函数名(实参列表)
- 1、调用的点.可以省略的,对象名|类名 函数名(实参列表)
- 2、如果实参列表为空,那么()可以省略,如果声明函数的时候没有加() 那么调用的时候一定不能加()
- 3、如果函数的实参列表只有一个 那么()也可以省略
对象名|类名 函数名 唯一的实参
-
/** * 函数的简化操作: * Scala信奉一个原则:至简原则 * 函数基于这个原则,我们就可以进行相应的简化:简化可以从声明和调用两个层面简化 * 声明的简化规则: * 1、如果函数的参数没有 那么()可以省略 * 2、函数的返回值不用加,可以根据函数的最后一行自动推断,最后一行返回的数据return关键字可以省略的 如果return没有省略 那么返回值类型一定要写上的 * 3、如果函数的返回值是unit类型的,那么返回值类型和=号都可以省略 * 4、如果函数的函数体只有一行 那么{}可以省略 * 5、如果函数名没有意义,那么函数名可以省略的(函数充当参数或者返回值使用的--高阶函数用法) * * 调用的简化规则: * 1、如果函数没有参数,那么调用的时候 括号可以加也可以不加,但是如果函数声明的时候没有加括号 那么调用的时候一定不能加括号 * 2、调用函数的时候 点可以省略的 如果参数只有一个 调用的时候()可以省略 */ object Demo12 { def main(args: Array[String]): Unit = { test Demo12 test4 1; } /** * 没有参数可以不用加括号 */ def test:Unit={} def test1(a:Int,b:Int):Int={ return a+b } def test2{} def test3(a:Int,b:Int)=a+b def test4(a:Int){} }
七、面向对象
Scala源于Java中,因此在Scala中也存在面向对象编程思想,面向对象编程最核心的逻辑就是以类来组织代码,以对象来调用代码。Scala的面向对象和Java基本上思维是一致的,只不过就是语法稍微不一样而已。
1、包和import导入
-
包package:包是用来分类管理Scala代码的,将不同类型的代码放到不同的包下,便于我们管理
-
Scala包有两种管理代码的方式
-
采用和Java一样的管理机制,新建包,包下可以新建类和子包
-
采用包对象的管理机制,实现一个文件中存在多个包和多个scala类
-
package com{ package nuckl{ class B{ } } class A{ } }
-
-
-
Scala中类的导入问题import
-
在当前Scala类中,如果要使用非本包下的代码,那么我们就得需要通过import关键字导入才能使用。
-
每一个Scala类默认导入三个包
- java.lang._
- scala._
- scala.Predef._
-
Scala类中导入有一些规则和Java有点不一样
- 1、Scala可以在任何位置进行导包操作,代码只能在导包位置之后使用
我们可以把所有的包放到package之后 class之前 - 2、如果我们要导入一个包下的所有代码,那么可以使用_当作通配符使用
- 3、我们现在只想导入某一个包下的两个类,而非所有类
import xxxx.{x1,x2} - 4、导包重命名操作:可以将一个类重命名为另外一个名字在当前类中使用
import xxxx.{x1=>x2} x1类在当前类中可以使用x2名字来替代 - 5、屏蔽某一个包下的部分类:导入一个包下的所有代码,除了某几个类之外
import xxxx{x1=>_,x2}
导入xxxx包下的x2类,不导入x1这个类
- 1、Scala可以在任何位置进行导包操作,代码只能在导包位置之后使用
-
package importstudy import java.util.{Date, Scanner} import java.sql.{Date => SQLDate} /** * 导包的规则 */ class Demo01 { def test():Unit = { var sc = new Scanner(System.in); var d = new Date() var f = new SQLDate(1) } }
-
2、面向对象中的类
-
类的定义
-
访问控制修饰符 class className 访问控制修饰符(主构造器参数列表){ 类体 }
-
访问控制符:三个 private protected public(不写)
-
-
在同一个Scala文件中可以存在多个Scala类,权限没要求的
3、面向对象中类的属性
属性用来描述类的特征
声明语法:访问控制修饰符 var|val 属性名:属性类型 = 值;
属性声明的时候必须加值,但是我不想给其他值,只想给默认值,那么值使用_ 来代替
【注意】val修饰的属性 不能赋予_ 默认值,必须给一个显示的值
package classstudy
import scala.beans.BeanProperty
class Demo01 {
@BeanProperty var name:String = _
@BeanProperty val age:Int = 1
}
class Demo02{
}
object Demo02{
def main(args: Array[String]): Unit = {
var a:Demo01 = new Demo01()
var b:Demo02 = new Demo02()
println(a.getName)
a.setName("kkkk")
println(a.getName)
println(a.getAge)
println(a)
}
}
属性前加一个注解@BeanProperty
4、面向对象中类的方法
-
方法就是函数,函数声明在类中,函数称之为方法
-
语法: 访问控制修饰符 def 方法名(形参列表):方法的返回值类型 ={ 方法体 }
5、面向对象中类的代码块和内部类
{} 类中类
6、面向对象中类的构造器
-
构造器是创建该类的实例对象的
-
Scala有两种构造器
-
1、主构造器:声明在类上的 class ClassName 访问控制修饰符 (形参列表)–主构造器
-
2、辅助构造器:声明在类中的构造器,
-
语法: def this(形参列表){ }
-
-
辅助构造器必须在首行直接或者间接的调用主构造器代码
-
-
【注意】
-
主构造器不存在构造体,那么也就意味着主构造器无法给属性赋值,为了可以给属性赋值,主构造器的形参列表有个变化,主构造器的形参列表可以使用var或者val修饰,一旦主构造器的形参使用var val修饰 那么主构造器中的形参就是类的属性了
-
主构造器我们一般都是要求是无参构造器,而且主构造器一旦是无参的,那么主构造器的()可以省去
-
package constructor class People private ( var name:String,var age:Int){ def this(name:String){ this(name,1) } } class Student private{ } object People { def main(args: Array[String]): Unit = { val people = new People("zs",21) println(people.name) println(people.age) //val people1 = new People("zs") } }
-
7、Scala中对象的创建
对象的创建就是变量|常量的定义
var|val 对象名:类 = new 类名(实参)
简化:类型可以省略,自动推断;如果调用的构造器的实参为空,那么()也可以省略的。
8、面向对象的特征
-
封装性
-
和Java的封装一样的概念,合理隐藏、合理暴露,控制类、属性、方法、构造器、内部类能被谁访问使用,不能被谁访问使用。
-
Scala的封装权限只有三种:private protected 不写(public)
-
封装性和Java的区别在于两个地方
- protected的权限比Java的权限更加严格:只有当前类和子类能访问,同包下的类都无法访问。
- private的权限是私有化的,但是Scala开后门,private[包名] 只有当前类和指定包下能访问了,包一般是一个根包。
-
package encapsulation.demo class Demo01 private[encapsulation]{ } ==================================================== package encapsulation.demo01 import encapsulation.demo.Demo01 object Demo02 { def main(args: Array[String]): Unit = { val d = new Demo01() println(d) } }
-
-
继承性
-
和Java的概念是一样的,extends继承,Scala也是单继承,一旦继承父类,拥有父类中非私有化的属性和方法。
-
package inheritance class Father { private var name:String = _ protected var age:Int = _ var sex:String = _ private def test1(): Unit = { } protected def test2(): Unit = { } def test3(): Unit = { } } class Son extends Father{ } object Son{ def main(args: Array[String]): Unit = { var son = new Son(); println(son.age) println(son.sex) son.test2() son.test3() } }
-
-
多态性
-
定义变量的时候,分为编译时类型和运行时类型
-
向上转型:将儿子的对象给了父类的变量引用,可以直接给,不需要任何多余的操作。
var s:Father = new Son() -
向下转型:将父亲给儿子,不能直接给,需要使用asInstanceof[子类] 赋值,要求父亲的运行时类型必须是儿子或者儿子的子类类型。
var s:Father = new Son()
var s1:Son = s.asInstanceof[Son] -
如果我们要进行向下转型,最好先使用Scala提供的以下代码先判断父类的运行时类型是不是子类的类型。
对象名.isInstanceof[ClassName] -
classof[类名] 获取一个类的运行时类 返回的是一个Class类的对象
-
object Son{ def main(args: Array[String]): Unit = { /** * 向上转型可以直接转换 */ var son:Father = new Son() son.test3() /** * 向下转型 需要强转 asInstanceof[Son] */ var son1:Son = son.asInstanceOf[Son] println(son1) var son2:Father = new Father() if(son2.isInstanceOf[Son]){ var son3: Son = son2.asInstanceOf[Son] println(son3) }else{ println("son2不是Son的类型,无法向下转型") } } } package review class Demo02{ } object Demo02 { def main(args: Array[String]): Unit = { var a = new Demo02 val clazz = a.getClass println(clazz)//class review.Demo02 val value: Class[Demo02] = classOf[Demo02] println(value)//class review.Demo02 } }
-
-
抽象性
-
抽象类
-
abstract class ClassName{ 抽象属性 抽象方法 }
-
-
抽象属性
- 没有 = 赋值的属性就是抽象属性
-
抽象方法
- 没有函数体的方法称为抽象方法
-
Scala中非抽象子类重写抽象类的属性和方法时,为了表示重写的概念,加了override关键字表示重写的过程
package abstractstudy abstract class Animal (){ //抽象属性 抽象属性也是要被非抽象子类重写的 var name:String var age:Int = 1 //抽象方法 就是没有函数体的方法 def test():Unit def test1():Unit={ } } class Dog extends Animal{ override var name: String = _ override def test(): Unit = { } } object Dog{ def main(args: Array[String]): Unit = { val dog = new Dog dog.test() println(dog.name) // val animal = new Animal(); } }
-
-
Scala中伴生对象Object
-
Object定义的属性和方法就是当前Class类的静态内容,静态内容可以直接使用类名来调用
-
Object类中存在一个功能特别强大的方法,apply方法
-
apply方法可以让我们创建对象的时候不需要使用new关键字,而是直接使用类名(形参列表)的形式构建类的对象。
-
def apply(形参列表):Class ={ new ClassName(实参); }
-
-
package objectstudy import objectstudy.Demo01.age class Demo01 { def this(name:String){ this() this.name = name } var name:String = _ def test():Unit = { println(age) test() } } object Demo01{ def main(args: Array[String]): Unit = { var d = Demo01() var d1 = Demo01("kl") println(d1) println(d1.name) } var age:Int = _ def test01():Unit={ //println(name) } def apply():Demo01 = { new Demo01 } def apply(name:String):Demo01={ new Demo01(name) } }
-
-
Scala中的case class样例类
-
样例类就等同于Java中JavaBean,同时Scala提供case class是为了丰富模式匹配的功能的。
-
样例类的语法
- case class(形参列表) extends Father with T1
- 形参列表既是类的属性 还是类的主构造器,而且形参列表默认是使用private val修饰符的 ,同时样例类自动给我们生成类的toString、hashCode、equals、apply方法。
- 和模式匹配结合使用的。
-
package objectstudy import scala.beans.BeanProperty object Demo02 { def main(args: Array[String]): Unit = { var p:People = new People("zs",20) var p1:People = People("zs",20) println(p)//People(zs,30) println(p1)//People(ls,20) println(p == p1)//true println(p.getName)//zs println(p.setName("kl"))//() println(p.getName)//kl } } case class People(@BeanProperty var name:String, age:Int){ }
-
package caseclass abstract class Traffic{ } case class Car(pinpai:String,pailiang:Double) extends Traffic case class Plane(rongliang:Int) extends Traffic /** * 样例类和模式匹配的结合使用 */ object Demo{ def judgeTraffic(traffic: Traffic):Unit = { traffic match { case Car(a,b) if b>1.2 => println(s"这是一个小汽车给,品牌是$a,排量是$b") case Plane(c) => println(s"这是一个飞机,容量是$c") case _ => println("这个交通工具不识别") } } def main(args: Array[String]): Unit = { var traffic:Traffic = Car("问界M7",1.0) var traffic1:Traffic = Plane(6) judgeTraffic(traffic) judgeTraffic(traffic1) } }
-
-
Scala中的特质Trait
-
特质就是Java中的接口,只不过在Java中称之为接口,在Scala中称之为特质。Java中的所有接口都可以当作Scala的特质使用。
-
创建语法
-
trait 特质名{ 抽象属性 抽象方法 普通的属性 普通的方法 }
-
-
使用
- Scala中特质也是需要继承的,父类也是继承的
class Son extends Father with Trait1 with trait2
- Scala中特质也是需要继承的,父类也是继承的
-
package traitstudy
trait FlyAble {
//抽象属性
var name:String
//抽象方法
def fly():Unit
//普通的属性
var age:Int = _
//普通方法
def test():Unit ={
}
}
class Animal{}
class Bird extends Animal with FlyAble with Comparable[Bird]{
override var name: String = _
override def fly(): Unit = {
}
override def compareTo(o: Bird): Int = {
return 1
}
}
八、集合
Scala中可以使用Java的集合,但是Scala也给我们提供了一套集合体系,Spark Kafka既支持Scala集合体系,还支持Java集合体系。
1、Java集合体系
- Collection:单列集合(一行数据只有一列)
- List:元素可重复、且元素加入有序、可以根据索引取值
- Vector
- ArrayList
- LinkedList
- Set:元素不可重复、没有索引的概念
- HashSet:元素无序
- LinkedHashSet:元素加入有序
- TreeSet:元素是大小有序,需要用到比较器
- List:元素可重复、且元素加入有序、可以根据索引取值
- Map:双列集合,key值不可重复,value可重复
- HashMap
- HashTable
- Properties
- LinkedHashMap
- TreeMap
2、Scala集合体系
-
不可变集合体系:每一次操作集合都是返回一个新的集合对象,而原有的集合不受任何的影响
包:scala.collection.immutable-
Set:不可重复
-
Map:双列
-
Seq:可以重复
-
Set、Map 是 Java 中也有的集合 ;Seq 是 Java 没有的,我们发现 List 归属到 Seq 了,因此这里的 List 就和 Java 不是同一个概念了
IndexedSeq 和 LinearSeq 的区别:
(1)IndexedSeq是通过索引来查找和定位,因此速度快,比如 String 就是一个索引集合,通过索引即可定位
(2)LinearSeq 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
-
-
可变集合体系:每一次操作集合都是对原有的集合对象进行操作,而不是返回新的集合对象
包:scala.collection.mutable- Set
- Map
- Seq
3、Scala集合的使用
-
Scala中数组-Seq的使用
-
不可变数组:Array
-
可变数组:ArrayBuffer
-
package collection import scala.collection.mutable.ArrayBuffer /** * 数组的定义和使用 */ object ArrayStudy { def main(args: Array[String]): Unit = { /** * 1、不可变数组的创建 * (1)new 创建 * (2)Array提供的伴生对象的apply方法来创建 */ var array:Array[Int] = new Array[Int](10) array(0) = 1 println(array(0))//1 var array1 = Array(1,2,3,4,5,6,7,8,9,10) println(array1(1))//2 /** * 2、可变数组 ArrayBuffer */ var ab:ArrayBuffer[Any] = ArrayBuffer[Any]() ab.append(1) ab.append(2) ab.append(3) println(ab)//ArrayBuffer(1, 2, 3) /** * 3、可变数组与不可变数组的互相转换 */ array1.toBuffer //不可变数组转可变数组 ab.toArray //可变数组转不可变数组 /** * 4、多维数组 */ val array2:Array[Array[Int]] = Array.ofDim(2, 3) println(array2(0)(1))//0 //数组的遍历 for(elem:Array[Int] <- array2;a <- elem){ } for(i <- 0 to array2.length-1){ } } }
-
-
Scala中List列表的使用
-
不可变列表:List
-
可变列表:ListBuffer
-
package collection import scala.collection.mutable.ListBuffer object ListStudy { def main(args: Array[String]): Unit = { /** * 1、不可变列表 * List的伴生对象的apply方法构建的 */ var list:List[Int] = List[Int](1,2,3) //:: list集合的一种特殊运算符 代表在集合的头部添加一个新的元素 而且会给我们返回一个新的集合 //:: 可以添加多个元素 list = 4 :: 5 :: 6 :: list//List(4, 5, 6, 1, 2, 3,) //如果添加单个元素 使用 +: :+ list = list :+ 7//尾部添加 list = 8 +: list//头部添加 //List(8, 4, 5, 6, 1, 2, 3, 7) var list1:List[Int] = List[Int](10,11) //:::(扁平化操作) 将左边的集合中元素一个一个加到右边的集合中 而不是将左边的集合当作一个整体添加 list = list1 ::: list//List(10, 11, 8, 4, 5, 6, 1, 2, 3, 7) println(list)//List(8, 4, 5, 6, 1, 2, 3, 7) var list2 = 4 :: 5 ::List(1,2,3) :: Nil//List(4, 5, List(1, 2, 3)) var list3 = 4 :: 5 ::List(1,2,3) ::: Nil//List(4, 5, 1, 2, 3) println(list3) //移除左边的多少个元素 list3 = list3.drop(3)//List(2, 3) list3 = list3.updated(0,22)//List(22, 3) println(list3) println(list3(0))//22 //遍历数组 list3.foreach((a:Int)=>{println(a)})//22 3 /** * 可变ListBuffer */ var lb = ListBuffer[Int](1,2,3,4) //添加数据 lb.append(5)//默认添加到末尾 lb.insert(1,6)//默认添加到开头 lb.+=(7)//默认添加到末尾 println(lb)//ListBuffer(1, 6, 2, 3, 4, 5, 7) //移除数据 lb.remove(1)//移除目标索引的数据 lb.-=(1)//移除目标值 lb -= 1 println(lb)//ListBuffer(2, 3, 4, 5, 7) //取值 println(lb(1))//3 //遍历元素 1、索引遍历 2、增强的for循环 3、foreach lb.foreach(println)// 2 3 4 5 7 //修改数据 lb.update(1, 56) println(lb)//ListBuffer(2, 56, 4, 5, 7) } }
-
-
Scala中Set集合的使用
-
不可变Set集合:Set
-
可变Set集合:Set
-
package collection object SetStudy { def main(args: Array[String]): Unit = { /** * 不可变的Set集合 */ //1、创建不可变Set集合 var set:Set[Int] = Set[Int](1,2,3,4,5,1,2)//HashSet(5, 1, 2, 3, 4) //2、set集合中添加数据 set = set + 6 //3、set集合删除数据 set = set - 1//HashSet(5, 6, 2, 3, 4) 减的是元素 println(set) /** * 可变的set集合 */ var set1 = scala.collection.mutable.Set[Int](1,2,3,4,5,6,5,6) //1、添加数据 set1.add(88)//HashSet(1, 2, 3, 4, 5, 6, 88) set1 += 99//HashSet(1, 2, 3, 99, 4, 5, 6, 88) //2、删除数据 set1.remove(88)//HashSet(1, 2, 3, 99, 4, 5, 6) set1 -= 3//HashSet(1, 2, 99, 4, 5, 6) println(set1) } }
-
-
Scala中元组Tuple
-
package collection object TupleStudy { def main(args: Array[String]): Unit = { /** * 元组是Scala中一种比较特殊的“集合”类型 元组也可以存放一组数据,但是这一组数据类型可以不一致 * 元组的存放的数据个数是有限制的,最小1个 最多22个 * 因此元组在Scala中有一个专业的叫法:一元组 .... 二十二元组 * Tuple1 * Tuple2 * ... * Tuple22 */ //元组的创建有三种方式: var t1:Tuple2[String,Int] = new Tuple2[String,Int]("zs",20) var t2:Tuple2[String,Int] = ("ww",20) var t3:Tuple2[String,Int] = Tuple2[String,Int]("ls",20) println(t1)//(zs,20) println(t2)//(ww,20) println(t3)//(ls,20) var t4:Tuple3[Int,Int,Int] = (1,2,3) println(t4)//(1,2,3) //元组的类型有两种声明方式:TupleN[xxx,xxx] (xxx,xxx) var t5:(String,Int) = new Tuple2[String,Int]("kl",20) println(t5)//(kl,20) /** * 获取元组中元素 * 元组提供了一个属性 _x * 在Scala当中map集合的kv可以当作二元组来处理(添加数据,foreach) */ println(t4._3)//3 println(t5._1)//kl println(t5._2)//20 } }
-
-
Scala中Map集合的使用
-
package collection object MapStudy { def main(args: Array[String]): Unit = { /** * 1、不可变的Map集合 */ var map:Map[String,Int] = Map[String,Int]("zs"->20,"ls"->30,"kl"->20,"zs"->40) println(map)//Map(zs -> 40, ls -> 30, kl -> 20) //添加数据 添加数据 需要传入一个二元组 Tuple2 map = map + (("hyf",18))//Map(zs -> 40, ls -> 30, kl -> 20, hyf -> 18) //删除数据 map = map.-("ls")//Map(zs -> 40, kl -> 20, hyf -> 18) //更新数据 map = map.updated("kl",23)//Map(zs -> 40, kl -> 23, hyf -> 18) // 获取key对应的value val value1:Int = map.getOrElse("hyf", 10)//18 val value2:Int = map.getOrElse("ww", 999)//999 println(value2) println(map) //遍历 for(elem <- map.keys){ println(map.get(elem).get)//40 23 18 } map.foreach((tuple:(String,Int)) => {println(s"key=${tuple._1} value=${tuple._2}")}) //key=zs value=40 //key=kl value=23 //key=hyf value=18 /** * 2、可变的Map集合 */ val map1 = scala.collection.mutable.Map("a" -> 1, "b" -> 2, "c" -> 3) println(map1)//HashMap(a -> 1, b -> 2, c -> 3) //向集合增加数据 map1 += ("d" -> 4) println(map1)//HashMap(a -> 1, b -> 2, c -> 3, d -> 4) // 将数值4添加到集合,并把集合中原值1返回 val maybeInt: Option[Int] = map1.put("a", 4) println (maybeInt.getOrElse(0))//1 println(map1)//HashMap(a -> 4, b -> 2, c -> 3, d -> 4) map1 += ("b" -> 44) // 这里会将键 "b" 的值更新为 44 或者添加一个新的键值对 println(map1)//HashMap(a -> 4, b -> 44, c -> 3, d -> 4) //删除数据 map1.-=("b", "c") println(map1)//HashMap(a -> 4, d -> 4) //修改数据 map1.update("d", 5) println(map1)//HashMap(a -> 4, d -> 5) map1("d") = 66 println(map1)//HashMap(a -> 4, d -> 66) //打印集合 map1.foreach((kv) => { println(kv) //(a,4) //(d,66) }) } }
-
4、Scala中集合的一些常用函数
-
基本函数
-
xxx.length —— 获取集合长度
-
xxx.size —— 获取集合大小
-
xxx.foreach(函数) —— 循环遍历
-
xxx.mkString(“分隔符”) —— 生成字符串
-
xxx.contains(xxxx) —— 是否包含
-
xxx.iterator ——迭代器
-
package collection import scala.collection.mutable.ListBuffer object Demo01 { def main(args: Array[String]): Unit = { /** * 测试集合的常用基本函数 */ var list1 = List[Int](1,2,3,4,5) println("-------不可变List-------") println(list1.length) println(list1.size) list1.foreach((a:Int)=>println(a)) list1.foreach(println)//简化形式 println(list1.mkString("==")) println(list1.contains(1)) var list2 = ListBuffer[Int](1,2,3,4,5) println("-------可变List-------") println(list2.length) println(list2.size) list2.foreach((a: Int) => println(a)) list2.foreach(println) //简化形式 println(list2.mkString("==")) println(list2.contains(1)) var map = Map[String, Int]("zs" -> 20, "ls" -> 20) println("-------可变Map-------") println(map.size) map.foreach(println) println(map.mkString("==")) println(map.contains("zs")) } }
-
-
集合的一些高级函数
-
获取集合的头 head()
-
获取集合的尾(不是头的就是尾) tail
-
集合最后一个数据
-
集合初始数据(不包含最后一个)
-
反转
-
取前(后)n 个元素
-
去掉前(后)n 个元素
-
并集
-
交集
-
差集
-
拉链
-
滑窗
-
package collection /** * 集合的高级函数 */ object Demo02 { def main(args: Array[String]): Unit = { var list = List[Int](1,2,3,4) println(list.head) println(list.tail) println(list.last) println(list.init) println(list.reverse) println(list.take(2)) println(list.takeRight(3)) println(list.drop(1)) println(list.dropRight(1)) var list1 = List[Int](4,5,6,7,8,9) println(list.union(list1)) println(list.concat(list1)) println(list.intersect(list1)) println(list.diff(list1)) println(list1.diff(list)) var col:List[(Int,Int)] = list.zip(list1) println(col) /** * 滑窗 */ var list2 = List[Int](1,2,3,4,5,6,7,8,9,10,11,12,13) var list3:Iterator[List[Int]] = list2.sliding(3,1) while(list3.hasNext){ println(list3.next()) } } }
-
-
集合的计算函数
-
sum求和
-
求乘积
-
最大值
-
最小值
-
排序
-
sorted(implicit ord: Ordering[B]):根据当前集合中的元素进行大小比较 然后去排序,前提是集合中的元素必须是可以比较大小的,如果不能比较大小,那么需要传递一个比较器,比较器返回一个int类型的值
-
sortedBy(f: xx=>xxx)((implicit ord: Ordering[xxx])):将集合中元素转换称为另外一种类型 然后根据转换的类型做比较
-
sortWith(f:(xx,xx)=>Boolean): 自定义元素的比较规则,返回一个boolean类型的值
-
package collection class People(var name: String ,var age: Int ){ override def toString: String = s"People{name=$name,age=$age}" } object People{ def main(args: Array[String]): Unit = { var p1 = new People("zs",20) var p2 = new People("ls",30) var p3 = new People("ww",18) var list:List[People] = List[People](p1,p2,p3) println(list) var list1 = list.sorted(new Ordering[People] { override def compare(x: People, y: People): Int = { if (x.age > y.age){ 1 }else{ -1 } } }) println(list1) var list2 = list.sorted((x:People,y:People)=>{ if (x.age>y.age){ 1 }else{ -1 } }) println(list2) var list3 = list.sorted((x:People,y:People)=>x.age-y.age) println(list3) val list4 = list.sortBy((x: People) => x.age) println(list4) var list5 = list.sortWith((x:People,y:People)=>{ if (x.age>y.age){ true }else{ false } }) println(list5) } }
-
-
package collection /** * 集合的计算函数 */ object Demo03 { def main(args: Array[String]): Unit = { var list = List[Int](1,3,4,5) println(list.sum) println(list.product) println(list.max) println(list.min) val list1 = list.sorted val list2 = list.sortBy((a: Int) => a) println(list1) println(list2) println(list.sortWith((a:Int,b:Int)=>a>b)) } }
-
九、隐式转换
扩展类的功能的,让原本两个毫无关系的类,也可以互相调用里面的方法,隐式转换的内容会自动触发
1、隐式转换函数
-
implicit def 函数名(类型):另外一个类型={ 如何创建另外一个类型 }
-
将输入类型转换称为输出类型,从而扩展某个类型的功能和方法
-
package implicstudy class Demo { def run():Unit= { } } class Demo01{ def call():Unit={ println("call方法执行了") } } object Demo{ def main(args: Array[String]): Unit = { implicit def test(demo:Demo):Demo01 ={ new Demo01() } var demo = new Demo() demo.call() } } package implicstudy class Test { def haha():Unit={ println("haha执行了") } } object Test{ def main(args: Array[String]): Unit = { implicit def xxx(a:Any):Test = { new Test } var a:Int = 1 a.haha() var b:Boolean = true b.haha() } }
2、隐式转换参数
-
可以实现在调用参数的时候,哪怕参数没有默认值,也可以不用传递,也能调用函数
-
如果使用隐式转换参数,需要做两步操作
- 1、首先需要将函数的参数声明为隐式转换参数
implicit 变量名:数据类型 - 2、在函数调用的作用域当中,声明一个同类型的隐式变量,这样的话调用函数的时候,函数会从作用域当中去找一个同类型的参数自动填充
implicit 变量名:数据类型 = 值 - 【注意】在同一个作用域下,不能存在两个同类型的隐式变量
- 1、首先需要将函数的参数声明为隐式转换参数
-
package implicstudy object Demo02 { def test(implicit name:String):Unit={ println(name) } def main(args: Array[String]): Unit = { implicit var name = "zs" //implicit var name1 = "zs" test } }
3、隐式转换类
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。
十、Scala中_的作用
1、_ 在模式匹配中,_代表的是未匹配到其他情况,默认情况
2、函数名 _ : _代表的是将函数当作一个整体来使用,而非调用函数
3、导包的时候_有两种用途
- 1、导入一个包下的所有类
import java.lang._ - 2、屏蔽某一个包下的指定类
import java.util.{Arrays=>_}
4、声明属性的时候,属性值可以使用_替代:此时_代表给属性赋予默认值