目录
a:面向对象:是一种存粹的面向对象语言,支持类和对象的定义,以及继承、多态和封装等面向对象的特性。
b:函数式编程:支持高阶函数、匿名函数(Lambda表达式)、柯里化等函数式编程特性。
c:静态类型:Scala是一种静态类型语言,意味着类型是在编译时就确定的。
是Scala中常用的一种数据结构,数组是一种存储了相同类型元素的固定大小的顺序集合。
var arr: Array[String] = new Array[String](num)
var arr:Array[String] = Array(元素1,元素2,…)
Scala中匿名函数是使用箭头“=>”定义的,箭头的左边是参数列表,箭头的右边是表达式,表达式将产生函数的结果。
通常可以将匿名函数赋值给一个常量或变量,再通过常量名或变量名调用该函数。
若函数中的每个参数在函数中最多只出现一次,则可以使用占位符“_”代替参数。
高阶函数经常将只需要执行一次的函数定义为匿名函数并作为参数。一般情况下,匿名函数的定义是“参数列表=>表达式”。
由于匿名参数具有参数推断的特性,即推断参数的数据类型,或根据表达式的计算结果推断返回结果的数据类型,因此定义高阶函数并使用匿名函数作为参数时,可以简化匿名函数的写法。
定义高阶函数计算矩形的周长,该函数传入一个Double类型的值作为参数,返回以一个Double类型的值作为参数的函数,如下图。
柯里化是指将接收多个参数的函数变换成接收单一参数(最初函数的第一个参数)的函数,新的函数返回一个以原函数余下的参数为参数的函数。
if语句、if…else语句、if…else if…else语句、if…else嵌套语句,语法格式如下。
if(布尔表达式) { 若布尔表达式为true,则执行该语句块}
if(布尔表达式) { 若布尔表达式为true,则执行该语句块}
if(布尔表达式2) { 若布尔表达式2为true,则执行该语句块
} else if(布尔表达式3) { 若布尔表达式3为true,则执行该语句块
} else { 若布尔表达式2为false且布尔表达式3为false,则执行该语句块
循环是指在某种条件下将一段代码按顺序重复执行。在Scala中有3种循环结构,分别为while循环、do…while循环和for循环。
多重循环是常见的for循环,多重循环也称为for循环嵌套,是指在两个或多个区间内循环反复,多个循环区间用分号隔开。
Scala可以在for循环中使用if判断过滤一些元素,多个过滤条件用分号隔开。
for循环使用yield可以将返回值作为一个变量存储,语法格式如下。
var retVar = for(var x <- List; if condition1; if condition2…) yield x
retVar是变量名,for关键字后的括号用于指明变量和条件,而yield会将每一次循环得到的返回值保存在一个集合中,循环结束后将返回该集合,并赋值给变量retVar。
使用yield对1~10的偶数进行记录,并保存至变量even中,如下图。
ØScala的列表(List)与数组非常相似,列表的所有元素都具有相同的类型。
Ø与数组不同的是,列表是不可变的,即列表的元素不能通过赋值进行更改。
Ø定义列表时,需要写明列表元素的数据类型,或者根据列表初值类型自动推断。具有类型T的元素的列表类型可写为List[T]。
Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
Scala合并两个列表时使用的是:::()或concat()方法,而合并两个集合使用的是++()方法。
Ø另外映射还可以通过keys方法获取所有的键,通过values方法获取所有值,也可以通过isEmpty方法判断映射的数据是否为空
Ø元组(Tuple)是一种类似于列表的结构,但与列表不同的是,元组可以包含不同类型的元素。
Ø目前,Scala支持的元组最大长度为22,即Scala元组最多只能包含22个元素。
Ø访问元组元素可以通过“元组名称._元素索引”进行,索引从1开始。
Ømap()方法可通过一个函数重新计算列表中的所有元素,并且返回一个包含相同数目元素的新列表。
Øforeach()方法和map()方法类似,但是foreach()方法没有返回值,只用于对参数的结果进行输出。
Øfilter()方法可以移除传入函数的返回值为false的元素。
Øflatten()方法可以将嵌套的结构展开,即flatten()方法可以将一个二维的列表展开成一个一维的列表。
ØflatMap()方法结合了map()方法和flatten()方法的功能,接收一个可以处理嵌套列表的函数,再对返回结果进行连接。
ØgroupBy()方法可对集合中的元素进行分组操作,返回的结果是一个映射。
ØScala是一种纯粹的面向对象语言,面向对象语言有两个重要的概念:类和对象。
Ø其中,类是对象的抽象,也可以把类理解为模板,对象才是真正的实体。
class ClassName(参数列表) extendst {}
Ø一个Scala源文件中可以有多个类,并且Scala类可以有参数。
Ø一般,Scala类名的第一个字母需要大写,如果需要使用几个单词构成一个类的名称,那么每个单词的第一个字母都要大写。
Ø与Java等其他语言不同的是,Scala中的类不定义为public。
ØScala只允许继承一个父类,并且继承父类的所有属性和方法。
Ø子类继承父类中已经实现的方法时,需要使用override关键字,子类继承父类中未实现的方法时,可以不用override关键字。
ØScala中没有static关键字,因此Scala的类中不存在静态成员。但是Scala可以使用object关键字实现单例模式。
ØScala中使用单例模式时需要使用object定义一个单例对象(object对象),单例对象在整个程序中只有一个实例。单例对象与类的区别在于单例对象不能带参数。
Ø当单例对象与某个类共享同一个名称时,单例对象被称作这个类的伴生对象,类被称为这个单例对象的伴生类。类和它的伴生对象可以互相访问对方的私有成员。
ØScala提供了强大的模式匹配机制。一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。模式和表达式之间用“=>”隔开。
Ø在Scala中,使用case关键字定义的类称为样例类。样例类是一种特殊的类,经过优化可应用于模式匹配。
ØScala编译器为样例类添加了一些语法上的便捷设定,具体如下。
•在伴生对象中提供了apply()方法,因此不使用new关键字也可以构造对象。
•编译器为样例类添加了toString()、hashCode()和equals()等方法。
ØScala不提供任何特殊文件写入能力,所以进行文件的写操作使用的是Java的I/O类中的PrintWriter来实现。
一:Scala定义以及特性
(1)Scala定义:
Scala是一种基于Java虚拟机(JVM)的编程语言,它结合了面向对象编程(OOP)和函数式编程(FP)的最佳特性。Scala的设计旨在提供一种简洁、高效、类型安全且具备强大表达能力的编程语言,使得开发者能够以更少的代码行进行高效的编程。
(2)Scala特性:
a:面向对象:是一种存粹的面向对象语言,支持类和对象的定义,以及继承、多态和封装等面向对象的特性。
b:函数式编程:支持高阶函数、匿名函数(Lambda表达式)、柯里化等函数式编程特性。
c:静态类型:Scala是一种静态类型语言,意味着类型是在编译时就确定的。
d:可扩展
二:Scala安装
a:在网页上运行Scala
b:Scala环境设置
c:Scala安装
1)在Linux和macOS系统上安装Scala
(2)在Windows系统上安装Scala
d:运行Scala
三:Scala方法
一:数据类型
Scala常用数据类型
数据类型 | 描述 |
Int | 32位有符号补码整数。数值区间为−32768~32767 |
Float | 32位IEEE754(IEEE浮点数算术标准)单精度浮点数 |
Double | 64位IEEE754(IEEE浮点数算术标准)双精度浮点数 |
String | 字符序列,即字符串 |
Boolean | 布尔值,true或false |
Unit | 表示无值,作用与Java中的void一样,是不返回任何结果的方法的结果类型。Unit只有一个实例值,写成() |
在Scala中,数据类型转换是一个常见的操作,它支持多种数据类型之间的转换,如字符串到整数、整数到浮点数、浮点数到双精度浮点数等。接下来,我将详细介绍Scala中常见的数据类型转换方法,并提供相应的代码示例。
字符串转换为整数
在Scala中,可以使用toInt
方法将字符串转换为整数。例如,若要将字符串"123"
转换为整数,可以使用以下代码:
val str = "123"
val num = str.toInt
println(num) // 输出:123
运行结果:
如果字符串不包含整数,则会抛出NumberFormatException
异常。为了避免这个异常,可以使用try
和catch
来捕获异常,并进行相应处理。
整数转换为浮点数
若要将整数转换为浮点数,可以使用toDouble
方法。例如,将整数123
转换为浮点数123.0
:
val num = 123
val doubleNum = num.toDouble
println(doubleNum) // 输出:123.0
运行结果:
浮点数转换为双精度浮点数
对于浮点数到双精度浮点数的转换,同样使用toDouble
方法即可。例如,将123.45
转换为123.45
:
val floatNum = 123.45
val doubleNum = floatNum.toDouble
println(doubleNum) // 输出:123.45
运行结果:
其他数据类型转换
Scala还支持其他类型的转换,如toFloat
、toLong
、toShort
、toByte
、toChar
等。例如,将整数123
转换为浮点数123.0
:
val num = 123
val floatNum = num.toFloat
println(floatNum) // 输出:123.0
运行结果:
将十六进制字符串转换为长整数Long
类型:
val hexString = "1A3B"
val longNum = hexString.toLong
println(longNum) // 输出:10995
隐式类型转换
Scala还支持隐式类型转换,这可以在不需要显式类型转换方法的情况下,自动将一种类型转换为另一种类型。例如,隐式地将String
类型转换为Int
类型:
val str = "123"
val num = implicitly[Int](str)
println(num) // 输出:123
使用asInstanceOf
进行类型转换
asInstanceOf
方法可以将一个值强制转换为指定的类型。例如,将Double
类型转换为Int
类型:
val doubleNum = 3.14
val intNum = doubleNum.asInstanceOf[Int]
println(intNum) // 输出:3
总结
Scala提供了丰富的数据类型转换方法,使得在不同类型之间进行转换变得十分方便。在使用这些方法时,需要注意可能会出现的类型不匹配问题,并合理使用异常处理机制来保证程序的健壮性。
object Test {
def main(args: Array[String]) = {
var a = 10;
var b = 20;
var c = 25;
var d = 25;
println("a + b = " + (a + b) );
println("a - b = " + (a - b) );
println("a * b = " + (a * b) );
println("b / a = " + (b / a) );
println("b % a = " + (b % a) );
println("c % a = " + (c % a) );
}
}在实际的编程实践中,可以根据具体的需求选择合适的数据类型转换方法。
二:定义与使用常量、变量
常量:
在程序运行过程中值不会发生变化的量为常量或值,常量通过val关键字定义,常量一旦定义就不可更改,即不能对常量进行重新计算或重新赋值。定义一个常量的语法格式如下。
val name: type = initialization
变量
变量是在程序运行过程中值可能发生改变的量。变量使用关键字var定义。与常量不同的是,变量定义之后可以重新被赋值。定义一个变量的语法格式如下。
var name: type = initialization
三:使用运算符:
运算符 | 意义 | 示例 | |
算术 运算符 | + | 两个数相加 | 1+2或1.+(2) |
− | 两个数相减 | 1−2或1. − (2) | |
* | 两个数相乘 | 1*2或1.*(2) | |
/ | 两个数相除 | 1/2或1./(2) | |
% | 两个数取余 | 1%2或1.%(2) | |
关系 运算符 | > | 判断左值是否大于右值,是则结果为真,否则结果为假 | 1>2或1.>(2) |
< | 判断左值是否小于右值,是则结果为真,否则结果为假 | 1<2或1.<(2) | |
>= | 判断左值是否大于等于右值,是则结果为真,否则结果为假 | 1>=2或1.>=(2) | |
<= | 判断左值是否小于等于右值,是则结果为真,否则结果为假 | 1<=2或1.<=(2) | |
== | 判断左值是否等于右值,是则结果为真,否则结果为假 | 1==2或1.==(2) | |
!= | 判断左值是否不等于右值,是则结果为真,否则结果为假 | 1!=2或1.!=(2) |
逻辑 运算符 | && | 若两个条件成立则结果为真,否则结果为假 | 1>2 && 2>3或1>2.&&(2>3) |
|| | 若两个条件有一个成立则结果为真,否则结果为假 | 1>2 || 2>3或1>2.||(2>3) | |
! | 对当前结果取反 | !(1>2) | |
位 运算符 | & | 参加运算的两个数据,按二进制位进行&运算,两位同时结果为1结果才为1,否则为0 | 0 & 1或0.&(1) |
| | 参加运算的两个数据,按二进制位进行|运算,两位只要有一个为1则结果为1 | 0 | 1或0.|(1) | |
^ | 参加运算的两个数据,按二进制位进行^运算,两位不同时结果为1,相同时结果为0 | 0^1或0.^(1) |
赋值 运算符 | = | 将右侧的值赋于左侧 | val a = 2 |
+= | 执行加法后再赋值左侧 | a += 2 | |
−= | 执行减法后再赋值左侧 | a-= 1 | |
*= | 执行乘法后再赋值左侧 | a *= 2 | |
/= | 执行除法后再赋值左侧 | a /= 3 | |
%= | 执行取余后再赋值左侧 | a %= 5 | |
<<= | 左移位后赋值左侧 | a <<= 2 | |
>>= | 右移位后赋值左侧 | a >>= 2 | |
&= | 按位&运算后赋值左侧 | a &= 2 | |
|= | 按位|运算后赋值左侧 | a |= 2 | |
^= | 按位^运算后赋值左侧 | a ^= 2 |
算术运算符
下表列出了 Scala 支持的算术运算符。
假定变量 A 为 10,B 为 20:
object Test {
def main(args: Array[String]) = {
var a = 10;
var b = 20;
var c = 25;
var d = 25;
println("a + b = " + (a + b) );
println("a - b = " + (a - b) );
println("a * b = " + (a * b) );
println("b / a = " + (b / a) );
println("b % a = " + (b % a) );
println("c % a = " + (c % a) );
}
}
关系运算符
下表列出了 Scala 支持的关系运算符。
假定变量 A 为 10,B 为 20:
object Test {
def main(args: Array[String]) = {
var a = 10;
var b = 20;
println("a == b = " + (a == b) );
println("a != b = " + (a != b) );
println("a > b = " + (a > b) );
println("a < b = " + (a < b) );
println("b >= a = " + (b >= a) );
println("b <= a = " + (b <= a) );
}
}
逻辑运算符
下表列出了 Scala 支持的逻辑运算符。
假定变量 A 为 1,B 为 0:
object Test {
def main(args: Array[String]) = {
var a = true;
var b = false;
println("a && b = " + (a&&b) );
println("a || b = " + (a||b) );
println("!(a && b) = " + !(a && b) );
}
}
位运算符
位运算符用来对二进制位进行操作,~,&,|,^ 分别为取反,按位与,按位或,按位异或运算,如下表实例:
A = 0011 1100
B = 0000 1101
-------位运算----------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
object Test {
def main(args: Array[String]) = {
var a = 60; /* 60 = 0011 1100 */
var b = 13; /* 13 = 0000 1101 */
var c = 0;
c = a & b; /* 12 = 0000 1100 */
println("a & b = " + c );
c = a | b; /* 61 = 0011 1101 */
println("a | b = " + c );
c = a ^ b; /* 49 = 0011 0001 */
println("a ^ b = " + c );
c = ~a; /* -61 = 1100 0011 */
println("~a = " + c );
c = a << 2; /* 240 = 1111 0000 */
println("a << 2 = " + c );
c = a >> 2; /* 15 = 1111 */
println("a >> 2 = " + c );
c = a >>> 2; /* 15 = 0000 1111 */
println("a >>> 2 = " + c );
}
}
赋值运算符
object Test {
def main(args: Array[String]) = {
var a = 10;
var b = 20;
var c = 0;
c = a + b;
println("c = a + b = " + c );
c += a ;
println("c += a = " + c );
c -= a ;
println("c -= a = " + c );
c *= a ;
println("c *= a = " + c );
a = 10;
c = 15;
c /= a ;
println("c /= a = " + c );
a = 10;
c = 15;
c %= a ;
println("c %= a = " + c );
c <<= 2 ;
println("c <<= 2 = " + c );
c >>= 2 ;
println("c >>= 2 = " + c );
c >>= a ;
println("c >>= a = " + c );
c &= a ;
println("c &= 2 = " + c );
c ^= a ;
println("c ^= a = " + c );
c |= a ;
println("c |= a = " + c );
}
}
运算符优先级取决于所属的运算符组,它会影响算式的的计算。
实例: x = 7 + 3 * 2; 这里, x 计算结果为 13, 而不是 20,因为乘法(*) 高于加法(+), 所以它先计算 3*2 再加上 7。
查看以下表格,优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。
类别 | 运算符 | 关联性 |
---|---|---|
1 | () [] | 左到右 |
2 | ! ~ | 右到左 |
3 | * / % | 左到右 |
4 | + - | 左到右 |
5 | >> >>> << | 左到右 |
6 | > >= < <= | 左到右 |
7 | == != | 左到右 |
8 | & | 左到右 |
9 | ^ | 左到右 |
10 | | | 左到右 |
11 | && | 左到右 |
12 | || | 左到右 |
13 | = += -= *= /= %= >>= <<= &= ^= |= | 右到左 |
14 | , | 左到右 |
四:定义与使用数组
数组
是Scala中常用的一种数据结构,数组是一种存储了相同类型元素的固定大小的顺序集合。
Scala
定义一个数组的语法格式如下。
# 第1种方式
var arr: Array[String] = new Array[String](num)
# 第2种方式
var arr:Array[String] = Array(元素1,元素2,…)
数组常用的方法
方法 | 描述 |
length | 返回数组的长度 |
head | 查看数组的第一个元素 |
tail | 查看数组中除了第一个元素外的其他元素 |
isEmpty | 判断数组是否为空 |
contains(x) | 判断数组是否包含元素x |
代码:
object p2 {
def main(args: Array[String]): Unit = {
// 字符串数组
val strArray = Array("Hello", "World", "Scala")
println(strArray.length) // 输出:3
println(strArray.head) // 输出:hello
println(strArray.tail.mkString("Array(", ", ", ")")) // 输出:world,scala
println(strArray.isEmpty) // 输出:false
println(strArray.contains("hello")) // 输出:false
// 整数数组
val intArray = Array(1, 2, 3)
println(intArray.length) // 输出:3
// 使用`size`方法可能会报错
val list = List(1, 2, 3)
println(list.size) // 报错:Int does not take parameters
}
}
运行结果:
五:定义与使用函数
1.匿名函数
匿名函数即在定义函数时不给出函数名的函数。
Scala中匿名函数是使用箭头“=>”定义的,箭头的左边是参数列表,箭头的右边是表达式,表达式将产生函数的结果。
通常可以将匿名函数赋值给一个常量或变量,再通过常量名或变量名调用该函数。
若函数中的每个参数在函数中最多只出现一次,则可以使用占位符“_”代替参数。
def main(args: Array[String]): Unit = {
// 使用匿名函数将列表中的每个元素平方
val numbers = List(1, 2, 3, 4, 5)
numbers.map((x: Int) => x * 2)
val squaredNumbers = numbers.map((x: Int) => x * x)
// 输出结果
squaredNumbers.foreach(println)
}
2.高阶函数—函数作为参数
高阶函数指的是操作其他函数的函数。
高阶函数可以将函数作为参数,也可以将函数作为返回值。
高阶函数经常将只需要执行一次的函数定义为匿名函数并作为参数。一般情况下,匿名函数的定义是“参数列表=>表达式”。
由于匿名参数具有参数推断的特性,即推断参数的数据类型,或根据表达式的计算结果推断返回结果的数据类型,因此定义高阶函数并使用匿名函数作为参数时,可以简化匿名函数的写法。
import scala.collection.immutable.List
object HighOrderFunctions {
def main(args: Array[String]): Unit = {
// 高阶函数的定义
val doubleSalary: Int => Int = (x: Int) => x * 2
// 使用高阶函数对薪资列表进行映射
val newSalaries: List[Int] = List(20000, 70000, 40000).map(doubleSalary)
// 输出结果
newSalaries.foreach(println)
}
}
3.高阶函数—函数作为返回值
高阶函数可以产生新的函数,并将新的函数作为返回值。
定义高阶函数计算矩形的周长,该函数传入一个Double类型的值作为参数,返回以一个Double类型的值作为参数的函数,如下图。
4.函数柯里化函数
柯里化是指将接收多个参数的函数变换成接收单一参数(最初函数的第一个参数)的函数,新的函数返回一个以原函数余下的参数为参数的函数。
定义两个整数相加的函数,一般函数的写法及其调用方式如下图。
使用函数柯里化
六: 使用if判断
Scala中的if判断根据复杂程度可分为
if语句、if…else语句、if…else if…else语句、if…else嵌套语句,语法格式如下。
# if语句
if(布尔表达式) { 若布尔表达式为true,则执行该语句块}
if (x == 30) {
if (y == 10) {
println("X = 30 and Y = 10")
}
}
# if…else语句
if(布尔表达式) { 若布尔表达式为true,则执行该语句块}
else { 若布尔表达式为false,则执行该语句块}
val number = 15
if (number > 10) {
println("Number is greater than 10")
} else {
println("Number is not greater than 10")
}
# if…else if…else语句
if(布尔表达式1) {
若布尔表达式1为true,则执行该语句块
} else if(布尔表达式2) {
若布尔表达式2为true,则执行该语句块
} else if(布尔表达式3) {
若布尔表达式3为true,则执行该语句块
}else {
若以上布尔表达式都为false,则执行该语句块
val score = 85
if (score >= 90) {
println("Excellent score!")
} else if (score >= 80) {
println("Good score!")
} else if (score >= 60) {
println("Passed score!")
} else {
println("Failed score.")
}
# if…else嵌套语句
if (布尔表达式1) {
if(布尔表达式2) { 若布尔表达式2为true,则执行该语句块
} else if(布尔表达式3) { 若布尔表达式3为true,则执行该语句块
} else { 若布尔表达式2为false且布尔表达式3为false,则执行该语句块
}
} else { 若以上条件都为false,则执行该语句块
}
val score = 85
if (score >= 90) {
println("Excellent grade!")
} else if (score >= 75) {
println("Good grade.")
} else if (score >= 60) {
println("Passing grade.")
} else {
println("Failing grade.")
}
七:使用for循环
循环是指在某种条件下将一段代码按顺序重复执行。在Scala中有3种循环结构,分别为while循环、do…while循环和for循环。
or循环是相对较为常用的一种循环,for循环语法格式如下。
for(变量<- 集合) {循环语句}
多重循环是常见的for循环,多重循环也称为for循环嵌套,是指在两个或多个区间内循环反复,多个循环区间用分号隔开。
Scala可以在for循环中使用if判断过滤一些元素,多个过滤条件用分号隔开。
for循环使用yield可以将返回值作为一个变量存储,语法格式如下。
var retVar = for(var x <- List; if condition1; if condition2…) yield x
retVar是变量名,for关键字后的括号用于指明变量和条件,而yield会将每一次循环得到的返回值保存在一个集合中,循环结束后将返回该集合,并赋值给变量retVar。
使用yield对1~10的偶数进行记录,并保存至变量even中,如下图。
八:定义与使用列表
ØScala的列表(List)与数组非常相似,列表的所有元素都具有相同的类型。
Ø与数组不同的是,列表是不可变的,即列表的元素不能通过赋值进行更改。
Ø定义列表时,需要写明列表元素的数据类型,或者根据列表初值类型自动推断。具有类型T的元素的列表类型可写为List[T]。
Ø构造列表的两个基本单位是“Nil”和“::”。
•“Nil”可以表示空列表;
•“::”称为中缀操作符,表示列表从前端扩展,遵循右结合。
列表操作常用方法
方法 | 描述 |
def head: A | 获取列表的第一个元素 |
def init:List[A] | 返回所有元素,除了最后一个元素 |
def last:A | 获取列表的最后一个元素 |
def tail:List[A] | 返回所有元素,除了第一个元素 |
def :::(prefix: List[A]): List[A] | 在列表开头添加指定列表的元素 |
def take(n: Int): List[A] | 获取列表前n个元素 |
def contains(elem: Any): Boolean | 判断列表是否包含指定元素 |
创建和操作列表
创建一个列表非常简单,可以直接使用 List()
构造函数,或者使用 ::
方法来递增式构建列表。
val list1 = List(1, 2, 3)
val list2 = 1 :: 2 :: 3 :: Nil
列表是不可变的,这意味着一旦创建了一个列表,就不能更改它的内容。如果需要修改列表,只能创建一个新的列表。
object p6 {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5)
val filteredList = list.filter(num => num % 2 == 0)
println(filteredList) // 输出: List(2, 4, 6)
println(filteredList.head)//输出List(1)
println(filteredList.init)//输出List(1,2,3,4)
println(filteredList.last )//输出List(5)
println(filteredList.tail)//输出List(2,3,4,5)
// println(filteredList.:::(prefix = List[1]))//
println(filteredList.take(3))//输出List(1,2,3)
println(filteredList.contains(9))//falase
}
}
九:定义与使用集合
Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
集合操作常用方法
方法 | 描述 |
def head: A | 获取集合的第一个元素 |
def init:Set[A] | 返回所有元素,除了最后一个 |
def last:A | 获取集合的最后一个元素 |
def tail:Set[A] | 返回所有元素,除了第一个 |
def ++(elems: A): Set[A] | 合并两个集合 |
def take(n: Int): List[A] | 获取列表前n个元素 |
def contains(elem: Any): Boolean | 判断集合中是否包含指定元素 |
Scala合并两个列表时使用的是:::()或concat()方法,而合并两个集合使用的是++()方法。
不可变集合的合并
不可变集合(如Set
、List
、Map
等)一旦被创建,就不能被修改,除非创建新的集合。要合并两个不可变集合,可以使用++
操作符或union
方法。
使用++
操作符
val set1 = Set(1, 2, 3)
val set2 = Set(3, 4, 5)
val mergedSet = set1 ++ set2
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)
在上面的代码中,mergedSet
将包含set1
和set2
中所有的唯一元素。
使用union
方法
val set1 = Set(1, 2, 3)
val set2 = Set(3, 4, 5)
val mergedSet = set1.union(set2)
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)
可变集合的合并
可变集合(如mutable.Set
、ListBuffer
、HashMap
等)允许在运行时动态添加、删除或修改元素。要合并两个可变集合,同样可以使用++
操作符或union
方法。
使用++
操作符
import scala.collection.mutable
val mutableSet1 = mutable.Set(1, 2, 3)
val mutableSet2 = mutable.Set(3, 4, 5)
val mergedSet = mutableSet1 ++ mutableSet2
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)
使用union
方法
import scala.collection.mutable
val mutableSet1 = mutable.Set(1, 2, 3)
val mutableSet2 = mutable.Set(3, 4, 5)
val mergedSet = mutableSet1.union(mutableSet2)
println(mergedSet) // 输出: Set(1, 2, 3, 4, 5)
注意事项
- 不可变集合的合并会生成新的集合对象,而原始集合保持不变。
- 可变集合的合并可以直接在原有集合上进行,更新后的集合仍然是可变的。
- 当处理大量数据时,考虑到性能优化,可以使用
SortedSet
代替普通的Set
来减少排序开销,或者使用HashSet
来减少重复元素检查的开销。 - 并行集合(
Parallel Collections
)也可以用来加速合并过程,特别是在多核处理器上。
十:定义与使用映射
Ø映射(Map)是一种可迭代的键值对结构。
Ø所有的值都可以通过键来获取,并且映射中的键都是唯一的。
Ø集合操作常用方法同样也适合映射。
Ø另外映射还可以通过keys方法获取所有的键,通过values方法获取所有值,也可以通过isEmpty方法判断映射的数据是否为空
不可变映射的使用
不可变映射(Map
)适用于不需要修改数据结构的情况。这类映射提供了一些方法来处理映射中的数据,比如 get
、update
、++
等等。
示例代码
val immutableMap = Map("a" -> 1, "b" -> 2)
val immutableMap2 = immutableMap + ("c" -> 3)
val immutableMap3 = immutableMap - "a"
val immutableMap4 = immutableMap.updated("d", 4)
val immutableMap5 = immutableMap.withDefaultValue(0)
在上述代码中,immutableMap
是一个不可变映射,我们通过 +
和 -
符号以及 update
方法来添加、删除和修改映射中的键值对。withDefaultValue
方法允许我们为一个映射提供一个默认值,这在处理可能不存在的键时很有用。
可变映射的使用
可变映射(mutable.Map
)允许在运行时修改映射的结构。可变映射提供了比不可变映射更为丰富的方法来处理数据。
示例代码
import scala.collection.mutable
val mutableMap = mutable.Map("a" -> 1, "b" -> 2)
mutableMap += ("c" -> 3)
mutableMap -= "a"
mutableMap.put("d", 4)
mutableMap.remove("e")
在这个例子中,我们创建了一个可变映射 mutableMap
,并通过 +=
、-=
、put
和 remove
方法进行了修改。
十一:定义与使用元组
Ø元组(Tuple)是一种类似于列表的结构,但与列表不同的是,元组可以包含不同类型的元素。
Ø元组的值是通过将单个的值包含在圆括号中构成的。
Ø目前,Scala支持的元组最大长度为22,即Scala元组最多只能包含22个元素。
Ø访问元组元素可以通过“元组名称._元素索引”进行,索引从1开始。
Ømap()方法可通过一个函数重新计算列表中的所有元素,并且返回一个包含相同数目元素的新列表。
Øforeach()方法和map()方法类似,但是foreach()方法没有返回值,只用于对参数的结果进行输出。
Øfilter()方法可以移除传入函数的返回值为false的元素。
Øflatten()方法可以将嵌套的结构展开,即flatten()方法可以将一个二维的列表展开成一个一维的列表。
ØflatMap()方法结合了map()方法和flatten()方法的功能,接收一个可以处理嵌套列表的函数,再对返回结果进行连接。
ØgroupBy()方法可对集合中的元素进行分组操作,返回的结果是一个映射。
十二:定义Scala类
ØScala是一种纯粹的面向对象语言,面向对象语言有两个重要的概念:类和对象。
Ø其中,类是对象的抽象,也可以把类理解为模板,对象才是真正的实体。
Ø一般定义类的格式如下。
class ClassName(参数列表) extendst {}
Ø一个Scala源文件中可以有多个类,并且Scala类可以有参数。
Ø一般,Scala类名的第一个字母需要大写,如果需要使用几个单词构成一个类的名称,那么每个单词的第一个字母都要大写。
Ø与Java等其他语言不同的是,Scala中的类不定义为public。
ØScala类继承一个类时需要使用关键字extends。
ØScala只允许继承一个父类,并且继承父类的所有属性和方法。
Ø子类继承父类中已经实现的方法时,需要使用override关键字,子类继承父类中未实现的方法时,可以不用override关键字。
class Student(val name: String, val age: Int) {
def this(name: String, age: Int, score: Int) = this(name, age)
def this(name: String) = this(name, 0)
def this() = this("Unknown", 0)
def updateAge(newAge: Int) = this.age = newAge
def toString = s"$name, $age"
}
object Test {
def main(args: Array[String]): Unit = {
val student = new Student("Alice", 20)
student.updateAge(25)
println(student.toString)
}
}
十三:使用Scala单例模式
ØScala中没有static关键字,因此Scala的类中不存在静态成员。但是Scala可以使用object关键字实现单例模式。
ØScala中使用单例模式时需要使用object定义一个单例对象(object对象),单例对象在整个程序中只有一个实例。单例对象与类的区别在于单例对象不能带参数。
Ø定义单例对象的语法如下。s
scala中实现单例模式的方法
使用object
关键字
在Scala中,你可以通过使用object
关键字来定义一个单例对象,它会自动继承其所在类的所有成员,包括字段和方法。单例对象在Scala中具有以下特点:
-
它是该类唯一的实例。
-
它在第一次被使用时创建,之后每次使用时直接调用。
-
它可以被赋予一个名字,这个名字可以用来直接调用单例对象的方法。
下面是一个简单的例子:
class MyClass {
def myMethod = "This is a method in MyClass"
}
object MyClass {
def myMethod = "This is a method in MyClass companion"
}
在上面的代码中,MyClass
类有一个名为myMethod
的方法,而MyClass
的伴生对象MyClass
也有一个同名的方法。你可以通过MyClass.myMethod
来调用这个方法,而不需要创建MyClass
的实例。
使用伴生对象
在Scala中,一个类可以有一个与之关联的伴生对象,这个伴生对象可以包含静态方法、静态字段等,与Java中的static
关键字类似。伴生对象在Scala中具有以下特点:
- 它与类有相同的名称。
- 它可以包含静态成员和方法。
- 它可以在类加载时初始化,也可以延迟初始化。
下面是一个包含伴生对象的例子:
class MyClass {
def myMethod = "This is a method in MyClass"
}
class MyClassCompanion {
def myMethod = "This is a method in MyClass companion"
}
object MyClassCompanion {
def myMethod = "This is a method in MyClass companion"
}
在这个例子中,MyClassCompanion
是MyClass
的伴生类,你可以通过MyClassCompanion.myMethod
来调用这个方法。
object ObjectName {}
Ø包含main方法的object对象可以作为程序的入口点。
Ø当单例对象与某个类共享同一个名称时,单例对象被称作这个类的伴生对象,类被称为这个单例对象的伴生类。类和它的伴生对象可以互相访问对方的私有成员。
Ø需要注意的是,必须在同一个源文件里定义类和它的伴生对象。
十四:使用Scala模式匹配
ØScala提供了强大的模式匹配机制。一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。模式和表达式之间用“=>”隔开。
Ø在Scala中,使用case关键字定义的类称为样例类。样例类是一种特殊的类,经过优化可应用于模式匹配。
ØScala编译器为样例类添加了一些语法上的便捷设定,具体如下。
•在伴生对象中提供了apply()方法,因此不使用new关键字也可以构造对象。
•样例类参数列表中的所有参数已隐式获得val关键字。
•编译器为样例类添加了toString()、hashCode()和equals()等方法。
case class Command(command: String)
val userInput = "help"
userInput match {
case Command("help") => println("Showing help.")
case Command("exit") => System.exit(0)
case _ => println("Invalid command.")
}