Spark第一步之学习Scala
文章目录
前言
一、Scala语言的特点
优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。
速度快:Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以速度会快很多。
能融合到Hadoop生态圈:Hadoop现在是大数据事实标准,Spark并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。
学习Scala编程语言,为后续学习Spark和Kafka奠定基础。
Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于JVM平台(Java虚拟机),并兼容现有的Java程序。
二、Scala与Java的关系是什么?
Scala与Java的关系是非常紧密的,因为Scala是基于JVM(JAVA虚拟机)的一门编程语言。所有Scala的代码,都需要经过编译为字节码,然后交由JVM来运行。
所以Scala和Java是可以无缝互操作的。Scala可以任意调用Java的代码。所以Scala与Java的关系是非常非常紧密的。
Scala是基于Java的,但却高于Java。
三、学习Scala
1.安装Scala
1.安装JDK以及Scala解释器并配置环境
因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK,我们这里安装的是JDK1.8版本。
http://www.scala-lang.org/
配置SCALA_HOME
配置PATH
验证安装配置是否成功
idea插件的安装(记得配置SDK就是你解压文件的路径)
2.Scala基本语法
在scala中,有以下数据类型。例如:
Byte、Char、Short、Int、Long、Float、Double、Boolean
貌似与Java的基本数据类型的包装类型相同,但是scala没有基本数据类型与包装类型的概念,统一都是类。scala自己会负责基本数据类型和引用类型的转换操作类型的加强版类型
Any是abstract类,它是Scala类继承结构中最底层的。所有运行环境中的Scala类都是直接或间接继承自Any这个类。
AnyRef是所有引用类型的基类。
AnyVal 所有值类型的基类。
Nothing是所有类的子类,是一个类。Nothing没有对象,但是可以用来定义类型。
Any:等价于java中的Object
AnyVal:等价于java中的
基本数据类型
AnyRef:等价于java中的
引用数据类型
Unit:等价于java中的void
nothing:任何类的子类
1、变量的定义
在Scala中声明一个变量:不管是什么类型的变量,在声明时统一使用var和val来声明
var :表示声明一个变量
val:表示声明一个常量 相当于java中的final
注意事项
- 声明变量时, 类型可以省略(编译器自动推导,即类型推导)
- 类型确定后, 就不能修改, 说明Scala 是强数据类型语言.
-
在声明/定义一个变量时, 可以使用var 或者 val 来修饰, var 修饰的变量可改变,
val 修饰的变量不可改 -
var 修饰的对象引用可以改变, val 修饰的则不可改变, 但对象的状态(值)却是
可以改变的。 - 变量声明时, 需要初始值。
程序中 +号的使用
- 当左右两边都是数值型时, 则做加法运算
- 当左右两边有一方为字符串, 则做拼接运算
scala> var str=" 琦"
str: String = 琦
scala> var age=18
age: Int = 18
scala> str="腾"
str: String = 腾
scala> val str3="方"
str3: String = 方
scala> str3="牛"
<console>:12: error: reassignment to val
str3="牛"
scala> var name="马"
name: String = 马
scala> var usename:String ="娜娜"
usename: String = 娜娜
scala> var s = 1
s: Int = 1
scala> var c = 2
c: Int = 2
scala> var b = s+ c
b: Int = 3
scala> var d = "m"
d: String = m
scala> var b = s+d
b: String = 1m
Scala的浮点型常量默认为Double型, 声明Float型常量, 须后加‘f’或‘F’
字符类型可以表示单个字符,字符类型是Char, 16位无符号Unicode字符(2个字节),
区间值为 U+0000 到 U+FFFF,字符常量是用单引号(‘ ’)括起来的单个字符。
object test {
def main(args: Array[String]): Unit = {
var c1 : Char = 'a'
var num : Int = 10 + c1 + 'b' //205
var c2 : Char = 'a' + 1//错误
println(num)
}
}
强制类型转换
Ø 介绍
自动类型转换的逆过程, 将容量大的数据类型转换为容量小的数据类型。 使用时要
加上强制转函数, 但可能造成精度降低或溢出,格外要注意。
Ø 案例演示
java : int num = (int)2.5
scala : var num : Int = 2.7.toInt //对象
标识符的命名规范
标识符概念
1) Scala 对各种变量、 方法、 函数等命名时使用的字符序列称为标识符
2) 凡是自己可以起名字的地方都叫标识符
标识符的命名规则(记住)
Scala中的标识符声明, 基本和Java是一致的, 但是细节上会有所变化。
1) 首字符为字母, 后续字符任意字母和数字, 美元符号, 可后接下划线_
2) 数字不可以开头。
3) 首字符为操作符(比如+ - * / ), 后续字符也需跟操作符 ,至少一个(反编译)
4) 操作符(比如+-*/)不能在标识符中间和最后.
5) 用反引号`....`包括的任意字符串, 即使是关键字(39个)也可以 [true]
hello // ok
hello12 // ok
1hello // error
h-b // error
x h // error
h_4 // ok
_ab // ok
Int // ok, 在scala中, Int 不是关键字, 而是预定义标识符,可以用, 但是不推荐
Float // ok
_
// 不可以, 因为在scala中, _ 有很多其他的作用, 因此不能使用
Abc // ok
+*- // ok
+a // error
2、基本操作符
运算符是一种特殊的符号, 用以表示数据的运算、 赋值和比较等。
- 算术运算符
- 赋值运算符
- 比较运算符(关系运算符)
- 逻辑运算符
-
位运算符
对于除号“/”, 它的整数除和小数除是有区别的: 整数之间做除法时, 只保留
整数部分而舍弃小数部分。 例如: var x : Int = 10/3 ,结果是 3
当对一个数取模时, 可以等价 a%b=a-a/b*b , 这样我们可以看到取模的一个本
质运算(和java 的取模规则一样)。
注意: Scala中没有++、 –操作符, 需要通过+=、 -=来实现同样的效果
3.流程控制
- 顺序控制
- 分支控制
- 循环控制
object test {
def main(args: Array[String]): Unit = {
var sumVal = 9
val result1 = {
if (sumVal > 20) {
"结果大于20"
}
}
sumVal = 90
println(result1) //()
val result2 =
if (sumVal > 20) {
"结果大于20"
}
println(result2) //结果大于20
}
}
for 循环
Scala 也为for 循环这一常见的控制结构提供了非常多的特性, 这些for 循环的
特性被称为for 推导式( for comprehension) 或for 表达式( for expression)
范围数据循环方式1
Ø 基本案例
for(i <- 1 to 3){
print(i + " ")
}println()
说明
- i 表示循环的变量, <- 规定好 to 规定
- i 将会从 1-3 循环, 前后闭合
范围数据循环方式2
Ø 基本案例
for(i <- 1 until 3) {
print(i + " ")
}println()
说明:
- 这种方式和前面的区别在于 i 是从1 到 3-1
- 前闭合后开的范围,和java的arr.length() 类似
循环守卫
Ø 基本案例
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}p
rintln()
2) 上面的代码等价
for (i <- 1 to 3) {
if (i != 2) {
println(i+"")
}
}
Ø 基本案例说明
-
循环守卫, 即循环保护式( 也称条件判断式, 守卫) 。 保护式为true则进入
循环体内部, 为false则跳过, 类似于continue
引入变量
Ø 基本案例
for(i <- 1 to 3; j = 4 - i) {
print(j + " ")
} Ø
2) 上面的代码等价
for ( i <- 1 to 3) {
val j = 4 –i
print(j+"")
}
对基本案例说明
- 没有关键字, 所以范围后一定要加; 来隔断逻辑
嵌套循环
Ø 基本案例
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
} Ø
对基本案例说明
1) 没有关键字, 所以范围后一定要加; 来隔断逻辑
2) 上面的代码等价
for ( i <- 1 to 3) {
for ( j <- 1to 3){
println(i + " " + j + " ")
}
}
循环返回值
Ø 基本案例
val res = for(i <- 1 to 10) yield i
println(res)
Ø 对基本案例说明
1) 将遍历过程中处理的结果返回到一个新Vector集合中, 使用yield关键字
注意事项和细节说明
- scala 的for循环形式和java是较大差异, 这点请同学们注意, 但是基本的原理还是一样的。
- scala 的for循环的步长如何控制! [for(i <- Range(1,3,2)]
while循环控制
注意事项和细节说明
- 循环条件是返回一个布尔值的表达式
- while循环是先判断再执行语句
- 与If语句不同, While语句本身没有值, 即整个While语句的结果是Unit类型的()
- 因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量 , 而变量需要声明在while循环的外部, 那么就等同于循环的内部对外部的变量造成了影响, 所以不推荐使用, 而是推荐使用for循环。
do…while循环控制
注意事项和细节说明
- 循环条件是返回一个布尔值的表达式
- do…while循环是先执行, 再判断
- 和while 一样, 因为do…while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量 , 而变量需要声明在do…while循环的外部,那么就等同于循环的内部对外部的变量造成了影响, 所以不推荐使用, 而是推荐使用for循环
Scala内置控制结构特地去掉了break和continue, 是为了更好的适应函数化
编程, 推荐使用函数式的风格解决break和contine的功能, 而不是一个关键字。
4、函数式编程
在学习Scala中将方法、 函数、 函数式编程和面向对象编程明确一下:
- 在scala中, 方法和函数几乎可以等同(比如他们的定义、 使用、 运行机制都一样的), 只是函数的使用方式更加的灵活多样。
-
函数式编程是从编程方式(范式)的角度来谈的, 可以这样理解: 函数式编程把函数当做一等公民, 充分利用函数、 支持的函数的多种使用方式。
比如:在Scala当中, 函数是一等公民, 像变量一样, 既可以作为函数的参数使用, 也可以将函数赋值给一个变量. , 函数的创建不用依赖于类或者对象, 而在Java当中,函数的创建则要依赖于类、 抽象类或者接口. - 面向对象编程是以对象为基础的编程方式。
- 在scala中函数式编程和面向对象编程融合在一起了 。
函数的定义
基本语法
def 函数名 ([参数名: 参数类型], …)[[: 返回值类型] =] {
语句…
return 返回值
}1
) 函数声明关键字为def (definition)
2) [参数名: 参数类型], …: 表示函数的输入(就是参数列表), 可以没有。 如果有, 多
个参数使用逗号间隔
3) 函数中的语句: 表示为了实现某一功能代码块
4) 函数可以有返回值,也可以没有
5) 返回值形式1: : 返回值类型 =
6) 返回值形式2: = 表示返回值类型不确定, 使用类型推导完成
7) 返回值形式3: 表示没有返回值, return 不生效
8) 如果没有return ,默认以执行到最后一行的结果作为返回值
函数注意事项和细节讨论
- 函数的形参列表可以是多个, 如果函数没有形参, 调用时 可以不带()
- 形参列表和返回值列表的数据类型可以是值类型和引用类型。 【案例演示】
- Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。 那么在这种情况下, return关键字可以省略
- 因为Scala可以自行推断, 所以在省略return关键字的场合, 返回值类型也可以省略
-
如果函数明确使用return关键字, 那么函数返回就不能使用自行推断了,这时要明
确写成 : 返回类型 = , 当然如果你什么都不写, 即使有return 返回值为()
def getSum(n1: Int, n2: Int): Int = {
n1 + n2
}
def getSum(n1: Int, n2: Int) = {
n1 + n2
}
def getSum(n1: Int, n2: Int): Int = {
//因为这里有明确的return , 这时 getSum 就不能省略 : Int = 的 Int了
-
如果函数明确声明无返回值(声明Unit) , 那么函数体中即使使用return关键字也不会有返回值
-
如果明确函数无返回值或不确定返回值类型, 那么返回值类型可以省略(或声明为Any)
-
Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活), 即: 函数中可以再声明/定义函数, 类中可以再声明类 , 方法中可以再声明/定义方法
-
Scala函数的形参, 在声明参数时, 直接赋初始值(默认值), 这时调用函数时, 如果没有指定实参, 则会使用默认值。 如果指定了实参, 则实参会覆盖默认值。
-
如果函数存在多个参数, 每一个参数都可以设定默认值, 那么这个时候, 传递的参数到底是覆盖默认值, 还是赋值给没有默认值的参数, 就不确定了(默认按照声明顺序[从左到右])。 在这种情况下, 可以采用带名参数
def mysqlCon(add:String = "localhost",port : Int = 3306,
user: String = "root", pwd : String = "root"): Unit = {
println("add=" + add)
println("port=" + port)
println("user=" + user)
println("pwd=" + pwd)
}
def f6 ( p1 : String = "v1", p2 : String ) {
println(p1 + p2);
}f
6("v2" ) // (?)
f6(p2="v2") // (?)
-
scala 函数的形参默认是val的, 因此不能在函数中进行修改.
12)递归函数未执行之前是无法推断出来结果类型, 在使用时必须有明确返回值类型
def f8(n: Int) = { //? 错误, 递归不能使用类型推断, 必须指定返回的数
据类型
if(n <= 0)
1
else
n * f8(n - 1)
}
-
Scala函数支持可变参数
说明:
(1) args 是集合, 通过 for循环 可以访问到各个值。
(2) 案例演示: 编写一个函数sum ,可以求出 1到多个int的和
(3) 可变参数需要写在形参列表的最后。
5、异常处理
Scala异常处理举例
try {
val r = 10 / 0
} catch {
case ex: ArithmeticException=> println("捕获了除数为零的算数异常")
case ex: Exception => println("捕获了异常")
} finally {
// 最终要执行的代码
println("scala finally...")
}
Scala异常处理小结
-
我们将可疑代码封装在try块中。 在try块之后使用了一个catch处理程序来捕获异
常。 如果发生任何异常, catch处理程序将处理它, 程序将不会异常终止。 -
Scala的异常的工作机制和Java一样, 但是Scala没有“checked(编译期)” 异常, 即
Scala没有编译异常这个概念, 异常都是在运行的时候捕获处理。 -
用throw关键字, 抛出一个异常对象。 所有异常都是Throwable的子类型。 throw
表达式是有类型的, 就是Nothing, 因为Nothing是所有类型的子类型, 所以
throw表达式可以用在需要类型的地方
def main(args: Array[String]): Unit = {
val res = test()
println(res.toString)
}d
ef test(): Nothing = {
throw new Exception("不对")
}
- 在Scala里, 借用了模式匹配的思想来做异常的匹配, 因此, 在catch的代码里, 是一系列case子句来匹配异常。 【前面案例可以看出这个特点, 模式匹配我们后面详解】 , 当匹配上后 => 有多条语句可以换行写, 类似 java 的 switch case x: 代码块…
- 异常捕捉的机制与其他语言中一样, 如果有异常发生, catch子句是按次序捕捉的。因此, 在catch子句中, 越具体的异常越要靠前, 越普遍的异常越靠后, 如果把越普遍的异常写在前, 把具体的异常写在后, 在scala中也不会报错, 但这样是非常不好的编程风格
- finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤, 一般用于对象的清理工作, 这点和Java一样。
- Scala提供了throws关键字来声明异常。 可以使用方法定义声明异常。 它向调用者函数提供了此方法可能引发此异常的信息。 它有助于调用函数处理并将该代码包含在try-catch块中, 以避免程序异常终止。 在scala中, 可以使用throws注释来声明异常
def main(args: Array[String]): Unit = {
f11()
}@
throws(classOf[NumberFormatException])//等同于NumberFormatException.class
def f11() = {
"abc".toInt
}
6.面向对象
基本语法
[修饰符] class 类名 {
类体
} Ø
定义类的注意事项
-
scala语法中, 类并不声明为public, 所有这些类都具有公有可见性(即默认就
是public),[修饰符在后面再详解]. - 一个Scala源文件可以包含多个类.
属性/成员变量
Ø 注意事项和细节说明
- 属性的定义语法同变量, 示例: [访问修饰符] var 属性名称 [: 类型] = 属性值
- 属性的定义类型可以为任意类型, 包含值类型或引用类型[案例演示]
- Scala中声明一个属性,必须显示的初始化, 然后根据初始化数据的类型自动推断, 属性类型可以省略(这点和Java不同)。 [案例演示]
- 如果赋值为null,则一定要加类型, 因为不加类型, 那么该属性的类型就是Null类型
class Person {
var age : Int = 10
var sal = 8090.9 //给属性赋初值, 省略类型, 会自动推导
var Name = null // Name 是什么类型?
var address : String = null // address 是什么类型?
}
- 如果在定义属性时, 暂时不赋值, 也可以使用符号_(下划线), 让系统分配默认值.
-
不同对象的属性是独立, 互不影响, 一个对象对属性的更改, 不影响另外一个。
如何创建对象
Ø 基本语法
val | var 对象名 [: 类型] = new 类型()
Ø 说明
- 如果我们不希望改变对象的引用(即: 内存地址), 应该声明为val 性质的, 否则声明为var, scala设计者推荐使用val ,因为一般来说, 在程序中, 我们只是改变对象属性的值, 而不是改变对象的引用。
- scala在声明对象变量时, 可以根据创建对象的类型自动推断, 所以类型声明可以省略, 但当类型和后面new 对象类型有继承关系即多态时, 就必须写了l
如何访问属性
Ø 基本语法
对象名.属性名;
方法
基本说明
Scala中的方法其实就是函数, 声明规则请参考函数式编程中的函数声明。
基本语法
def 方法名(参数列表) [: 返回值类型] = {
方法体
}
方法案例演示
给Cat类添加cal方法,可以计算两个数的和
class Dog {
private var sal: Double = _
var food : String = _
def cal(n1: Int, n2: Int): Int = {
return n1 + n2
}}
方法的调用机制原理
提示: 程序调用方法过程+说明
- 当我们scala开始执行时, 先在栈区开辟一个main栈。 main栈是最后被销毁
- 当scala程序在执行到一个方法时, 总会开一个新的栈。
- 每个栈是独立的空间, 变量(基本数据类型) 是独立的, 相互不影响(引用类型除外)
- 当方法执行完毕后, 该方法开辟的栈就会被jvm机回收。
构造器
基本介绍
构造器(constructor)又叫构造方法, 是类的一种特殊的方法, 它的主要作用是完成对新对象的初始化。
Scala构造器的介绍
和Java一样, Scala构造对象也需要调用构造方法, 并且可以有任意多个构造方法(即scala中构造器也支持重载) 。
Scala类的构造器包括: 主构造器 和 辅助构造器
Scala构造器的基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}//1. 辅助构造器 函数的名称this, 可以有多个, 编译器通过不同参数来区分.
Scala构造器注意事项和细节
- Scala构造器作用是完成对新对象的初始化, 构造器没有返回值。
- 主构造器的声明直接放置于类名之后 [反编译]
- 主构造器会执行类定义中的所有语句, 这里可以体会到Scala的函数式编程和面向对象编程融合在一起, 即: 构造器也是方法(函数) , 传递参数和使用方法和前面的函数部分内容没有区别【案例演示+反编译】
- 如果主构造器无参数, 小括号可省略, 构建对象时调用的构造方法的小括号也可以省略l 构造器Scala构造器注意事项和使用细节
- 辅助构造器名称为this(这个和Java是不一样的) , 多个辅助构造器通过不同参数列表进行区分, 在底层就是f构造器重载。
- 如果想让主构造器变成私有的, 可以在()之前加上private, 这样用户只能通过辅助构造器来构造对象了【反编译】
- 辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)
7、数据结构
scala集合基本介绍
- Scala同时支持不可变集合和可变集合, 不可变集合可以安全的并发访问
-
两个主要的包:
不可变集合: scala.collection.immutable
可变集合: scala.collection.mutable - Scala默认采用不可变集合, 对于几乎所有的集合类, Scala都同时提供了可变(mutable)和不可变(immutable)的版本
- Scala的集合有三大类: 序列Seq、 集Set、 映射Map, 所有的集合都扩展自Iterable特质, 在Scala中集合有可变(mutable)和不可变(immutable) 两种类型。
1)不可变集合: scala不可变集合, 就是这个集合本身不能动态变化。 (类似java的数组, 是不可以动态增长的)
2)可变集合:可变集合, 就是这个集合本身可以动态变化的。
1.Set、 Map是Java中也有的集合
2.Seq是Java没有的, 我们发现List归属到Seq了,因此这里的List就和java不是同一个概念了
小结
3.我们前面的for循环有一个 1 to 3 ,就是IndexedSeq 下的Vector
4.String也是属于IndexeSeq
5.我们发现经典的数据结构比如Queue 和 Stack被归属到LinearSeq
6.大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
7.IndexSeq 和 LinearSeq 的区别[IndexSeq是通过索引来查找和定位,因此速度快, 比如String就是一个索引集合, 通过索引即可定位][LineaSeq 是线型的, 即有头尾的概念,这种数据结构一般是通过遍历来查找,它的价值在于应用到一些具体的应用场景 (电商网站, 大数据推荐系统 :最近浏览的10个商品)
数组-定长数组(声明泛型)
第一种方式定义数组
这里的数组等同于Java中的数组,中括号的类型就是数组的类型
val arr1 = new Array[Int](10)
//赋值,集合元素采用小括号访问
arr1(1) = 7
val arr01 = new Array[Int](4)
println(arr01.length)
println("arr01(0)=" + arr01(0))
for (i <- arr01) {
println(i)
}
println("--------------------")
arr01(3) = 10
for (i <- arr01) {
println(i)
}
第二种方式定义数组
在定义数组时, 直接赋值
//使用apply方法创建数组对象
val arr1 = Array(1, 2)
var arr02 = Array(1, 3, "xxx")
for (i <- arr02) {
println(i)
}
数组-变长数组(声明泛型)
//定义/声明
val arr2 = ArrayBuffer[Int]()
//追加值/元素
arr2.append(7)
//重新赋值
arr2(0) = 7
//学习集合的流程(创建,查询,修改,删除)
案例演示+反编译
val arr01 = ArrayBuffer[Any](3, 2, 5)
println("arr01(1)=" + arr01(1))
for (i <- arr01) {
println(i)
}
println(arr01.length) //?
println("arr01.hash=" + arr01.hashCode())
arr01.append(90.0,13)
println("arr01.hash=" + arr01.hashCode())
arr01(1) = 89 //修改
println("--------------------------")
for (i <- arr01) {
println(i)
} //
删除
arr01.remove(0)
println("--------------------------")
for (i <- arr01) {
println(i)
}
println("最新的长度=" + arr01.length)
- ArrayBuffer是变长数组, 类似java的ArrayList
-
val arr2 = ArrayBuffer
Int
也是使用的apply方法构建对象 - def append(elems: A*) { appendAll(elems) } 接收的是可变参数.
- 每append一次, arr在底层会重新分配空间, 进行扩容, arr2的内存地址会发生变化, 也就成为新的ArrayBuffer
定长数组与变长数组的转换
arr1.toBuffer //定长数组转可变数组
arr2.toArray //可变数组转定长数组
说明:
-
arr2.toArray 返回结果才是一个定长数组,
arr2本身没有变化 -
arr1.toBuffer返回结果才是一个可变数组,
arr1本身没有变化
val arr2 = ArrayBuffer[Int]()
// 追加值
arr2.append(1, 2, 3)
println(arr2)
val newArr = arr2.toArray;
println(newArr)
val newArr2 = newArr.toBuffer
newArr2.append(123)
println(newArr2)
多维数组的定义和使用
Ø 说明
//定义
val arr = Array.ofDim[Double](3,4)
//说明: 二维数组中有三个一维数组,
每个一维数组中有四个元素
//赋值
arr(1)(1) = 11.11
Ø 案例演示
val array1 = Array.ofDim[Int](3, 4)
array1(1)(1) = 9
for (item <- array1) {
for (item2 <- item) {
print(item2 + "\t")
}
println()
}
println("===================")
for (i <- 0 to array1.length - 1) {
for (j <- 0 to array1(i).length - 1) {
printf("arr[%d][%d]=%d\t", i, j, array1(i)(j))
}
println()
}
数组-Scala数组与Java的List的互转
Scala数组转Java的List
在项目开发中, 有时我们需要将Scala数组转成Java数组, 看下面案例:
// Scala集合和Java集合互相转换
val arr = ArrayBuffer("1", "2", "3")
import scala.collection.JavaConversions.bufferAsJavaList
val javaArr = new ProcessBuilder(arr) //为什么可以这样使用?
val arrList = javaArr.command()
println(arrList) //输出 [1, 2, 3]
//案例演示+说明
补充:
trait MyTrait01 {}
class A extends MyTrait01 {}
object B {
def test(m: MyTrait01): Unit = {
println("b ok..")
}
}//
明确一个知识点
//当一个类继承了一个trait
//那么该类的实例, 就可以传递给这个t
val a01 = new A
B.test(a01)
数组-Scala数组与Java数组的互转
Java的List转Scala数组(mutable.Buffer)
在项目开发中, 有时我们需要将Java的List转成Scala数组, 看下面案例:
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
scalaArr.append("jack")
println(scalaArr)
//案例演示+说明
元组Tuple-元组的基本使用
基本介绍
元组也是可以理解为一个容器, 可以存放各种相同或不同类型的数据。说的简单点, 就是将多个无关的数据封装为一个整体, 称为元组, 最多的特点灵活,对数据没有过多的约束。
注意: 元组中最大只能有22个元素
元组的创建
val tuple1 = (1, 2, 3, "hello", 4)
println(tuple1)
访问元组中的数据,可以采用顺序号(_顺序号) , 也可以通过索引
(productElement) 访问。
应用案例
object Tupleo1 {
def main(args: Array[String]): Unit = {
val t1 = (1, "a", "b", true, 2)
println(t1._1) //访问元组的第一个元素 , 从1开始
println(t1.productElement(0)) // 访问元组的第一个元素, 从0开始
}
}
列表 List-创建List
基本介绍
Scala中的List 和Java List 不一样, 在Java中List是一个接口, 真正存放数据是ArrayList, 而Scala的List可以直接存放数据, 就是一个object, 默认情况下
Scala的List是不可变的, List属于序列Seq。
val List = scala.collection.immutable.List
object List extends SeqFactory[List]
创建List的应用案例
val list01 = List(1, 2, 3) //创建时, 直接分配元素
println(list01)
val list02 = Nil //空集合
println(list02)
创建List的应用案例小结
- List默认为不可变的集合
- List 在 scala包对象声明的,因此不需要引入其它包也可以使用
- val List = scala.collection.immutable.List
- List 中可以放任何数据类型, 比如 arr1的类型为 List[Any]
-
如果希望得到一个空列表, 可以使用Nil对象, 在 scala包对象声明的,因此不需
要引入其它包也可以使用
val Nil = scala.collection.immutable.Nil
基本介绍
向列表中增加元素, 会返回新的列表/集合对象。 注意: Scala中List元素的追加形式非常独特, 和Java不一样。
方式1-在列表的最后增加数据
案例演示
方式2-在列表的最前面增加数据
案例演示
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
val list2 = list1 :+ 4
println(list1) //list1没有变化
println(list2) //新的列表结果是 [1, 2, 3, "abc", 4]
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
val list2 = 4 +: list1
println(list1) //list1没有变化
println(list2) //新的列表结果是?
方式3-在列表的最后增加数据
Ø 说明:
- 符号::表示向集合中 新建集合添加元素。
- 运算时, 集合对象一定要放置在最右边,
- 运算规则, 从右向左。
-
::: 运算符是将集合中的每一个元素加入到空集合中去
Ø 应用案例:
案例演示+说明
val list1 = List(1, 2, 3, "abc")
val list5 = 4 :: 5 :: 6 :: list1 :: Nil
println(list5)
//下面等价 4 :: 5 :: 6 :: list1
val list7 = 4 :: 5 :: 6 :: list1 ::: Nil
println(list7)
//案例1 + 说明
ListBuffer
ListBuffer是可变的list集合, 可以添加, 删除元素,ListBuffer属于序
列//追一下继承关系即可
Seq var listBuffer = ListBuffer(1,2)
val lst0 = ListBuffer[Int](1, 2, 3)
println("lst0(2)=" + lst0(2))
for (item <- lst0) {
println("item=" + item)
}
val lst1 = new ListBuffer[Int]
lst1 += 4
lst1.append(5)
lst0 ++= lst1
val lst2 = lst0 ++ lst1
val lst3 = lst0 :+ 5
println("=====删除=======")
println("lst1=" + lst1)
lst1.remove(1)
for (item <- lst1) {
println("item=" + item)
}
队列 Queue-基本介绍
队列的说明
- 队列是一个有序列表, 在底层可以用数组或是链表来实现。
- 其输入和输出要遵循先入先出的原则。 即: 先存入队列的数据, 要先取出。后存入的要后取出
- 在Scala中, 由设计者直接给我们提供队列类型使用。
-
在scala中, 有 scala.collection.mutable.Queue 和
scala.collection.immutable.Queue , 一般来说, 我们在开发中通常使用可变集合中的队列
import scala.collection.mutable
//说明: 这里的Int是泛型, 表示q1队列只能存放Int类型
//如果希望q1可以存放其它类型, 则使用 Any 即可。
val q1 = new mutable.Queue[Int]
println(q1)
val q1 = new Queue[Int]
q1 += 20 // 底层?
println(q1)
q1 ++= List(2,4,6) //
println(q1)
//q1 += List(1,2,3) //泛型为Any才ok
println(q1)
队列 Queue-删除和加入队列元素
说明\按照进入队列的顺序删除元素(队列先进先出)
应用案例
q1.dequeue()
println(q1)
val q1 = new mutable.Queue[Int]//
q1 += 12
q1 += 34
q1 ++= List(2,9)
q1.dequeue() //队列头
println(q1)
q1.enqueue(20,60) //队列位
println(q1)
队列 Queue-给队列添加元素
说明按照队列的算法, 会将数据添加到队列的最后。
应用案例
q1.enqueue(9, 8, 7)
println(q1)
队列 Queue-返回队列的元素
//返回队列的第一个元素
println(q1.head)
//返回队列最后一个元素
println(q1.last)
//返回队列的尾部
//即: 返回除了第一个以外剩余的元素, 可以级联使用, 这个在递归时使用较多。
println(q1.tail)
println(q1.tail.tail)
映射 Map-基本介绍
Scala中的Map介绍
-
Scala中的Map 和Java类似, 也是一个散列表, 它存储的内容也是键值对
(key-value)映射, Scala中不可变的Map是有序的, 可变的Map是无序的。 -
Scala中, 有可变Map (scala.collection.mutable.Map) 和 不可变
Map(scala.collection.immutable.Map)
映射 Map-构建Map
方式1-构造不可变映射
Scala中的不可变Map是有序, 构建Map中的元素底层是Tuple2类型。
方式2-构造可变映射
Ø 案例
val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> "北京")
Ø 小结
1.从输出的结果看到, 输出顺序和声明顺序一致
2.构建Map集合中, 集合中的元素其实是Tuple2类型
3.默认情况下(即没有引入其它包的情况下) ,Map是不可变map
方式2-构造可变映射
//需要指定可变Map的包
val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
说明
1.从输出的结果看到, 输出顺序和声明顺序不一致
方式3-创建空的映射
val map3 = new scala.collection.mutable.HashMap[String, Int]
println(map3)
方式4-对偶元组
即创建包含键值对的二元组, 和第一种方式等价, 只是形式上不同而已。
对偶元组 就是只含有两个数据的元组。
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30) )
println("map4=" + map4)
println(map4("A"))l 映射 Map-取值
方式1-使用map(key)
val value1 = map2("Alice")
println(value1)
说明:
- 如果key存在, 则返回对应的值
- 如果key不存在, 则抛出异常[java.util.NoSuchElementException]
- 在Java中,如果key不存在则返回nulll 映射 Map-取值
方式2-使用contains方法检查是否存在key
// 返回Boolean
// 1.如果key存在, 则返回true
// 2.如果key不存在, 则返回false
map4.contains("B")
说明:
使用containts先判断在取值, 可以防止异常, 并加入相应的处理逻辑
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30.9) )
if( map4.contains("B") ) {
println("key存在 值= " + map4("B"))
} else {
println("key不存在")
}
映射 Map-取值
方式3-使用map.get(key).get取值
通过 映射.get(键) 这样的调用返回一个Option对象, 要么是Some, 要么是None
说明和小结:
- map.get方法会将数据进行包装
- 如果 map.get(key) key存在返回some,如果key不存在, 则返回None
- 如果 map.get(key).get key存在, 返回key对应的值,否则, 抛出异常
java.util.NoSuchElementException: None.get
var map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.get("A")) //Some
println(map4.get("A").get) //得到Some在取出l
方式4-使用map4.getOrElse()取值
getOrElse 方法 : def getOrElse[V1 >: V](key: K, default: => V1)
说明:
- 如果key存在, 返回key对应的值。
-
如果key不存在, 返回默认值。 在java中底层有很多类似的操作。
如何选择取值方式建议 - 如果我们确定map有这个key ,则应当使用map(key), 速度快
-
如果我们不能确定map是否有key ,而且有不同的业务逻辑, 使用
map.contains() 先判断在加入逻辑 -
如果只是简单的希望得到一个值, 使用map4.getOrElse(“ip”,“127.0.0.1”)
val map4 = mutable.Map( (“A”, 1), (“B”, “北京”), (“C”, 3) )
println(map4.getOrElse(“A”,“默认”))l 映射 Map-对map修改、 添加和删除
更新map的元素
案例:
说明: - map 是可变的, 才能修改, 否则报错
-
如果key存在: 则修改对应的值,key不存在,等价于添加一个key-val
val map4 = mutable.Map( (“A”, 1), (“B”, “北京”), (“C”, 3) )
map4(“AA”) = 20
println(map4)l 映射 Map-对map修改、 添加和删除
添加map元素
Ø 方式1-增加单个元素
Ø 方式2-增加多个元素
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4 += ( "D" -> 4 )
map4 += ( "B" -> 50 )
println(map4)
思考: 如果增加的key 已经存在会怎么样?
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
val map5 = map4 + ("E"->1, "F"->3)
map4 += ("EE"->1, "FF"->3)
映射 Map-对map修改、 添加和删除
删除map元素
说明
- “A”,“B” 就是要删除的key, 可以写多个.
- 如果key存在, 就删除, 如果key不存在, 也不会报错.
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4 -= ("A", "B")
println("map4=" + map4)l 映射 Map-对map遍历
对map的元素(元组Tuple2对象 )进行遍历的方式很多, 具体如下:
val map1 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
for ((k, v) <- map1) println(k + " is mapped to " + v)
for (v <- map1.keys) println(v)
for (v <- map1.values) println(v)
for(v <- map1) println(v) //v是Tuple?
说明
1.每遍历一次, 返回的元素是Tuple2
2.取出的时候, 可以按照元组的方式来取l 集 Set-基本介绍
集是不重复元素的结合。 集不保留顺序, 默认是以哈希集实现
Java中Set的回顾
java中, HashSet是实现Set接口的一个实体类, 数据是以哈希表的形式存放的, 里面的不能包含重复数据。 Set接口是一种不包含重复元素的 collection,HashSet中的数据也是没有顺序的。
案例演示:
Scala中Set的说明
默认情况下, Scala 使用的是不可变集合, 如果你想使用可变集合, 需要引用
scala.collection.mutable.Set 包
HashSet hs = new HashSet<String>();
hs.add("jack");
hs.add("tom");
hs.add("jack");
hs.add("jack2");
System.out.println(hs);l 集 Set-创建
Set不可变集合的创建
Set可变集合的创建
val set = Set(1, 2, 3) //不可变
println(set)
import scala.collection.mutable.Set
val mutableSet = Set(1, 2, 3) //可变
import scala.collection.mutable
object ScalaSet01 {
def main(args: Array[String]): Unit = {
val set01 = Set(1,2,4,"abc")
println(set01)
val set02 = mutable.Set(1,2,4,"abc")
println(set02)
}
}
集 Set-可变集合的元素添加和删除
可变集合的元素添加
说明: 如果添加的对象已经存在, 则不会重复添加, 也不会报错
mutableSet.add(4) //方式1
mutableSet += 6 //方式2
mutableSet.+=(5) //方式3
val set02 = mutable.Set(1,2,4,"abc")
set02.add(90)
set02 += 78
set02 += 90
println(set02)l 集 Set-可变集合的元素添加和删除
可变集合的元素删除
val set02 = mutable.Set(1,2,4,"abc")
set02 -= 2 // 操作符形式
set02.remove("abc") // 方法的形式, scala的Set可以直接删除值
println(set02)
说明: 说明: 如果删除的对象不存在, 则不生效, 也不会报错l 集 Set-遍历
集Set的遍历
val set02 = mutable.Set(1, 2, 4, "abc")
for(x <- set02) {
println(x)
}
问题
为maven添加scala插件
添加Sources Root Directory
总结