四天掌握Scala(3)
1.9 方法调用方式
在 Scala 中,有以下几种方法调用方式,后缀调用法、中缀调用法、花括号调用法、无括号调用法,在后续编写 Spark、Flink 程序时,会使用到这些方法调用方式。
1.9.1 后缀调用法
这种方法与 Java 没有区别。
语法:
对象名.方法名(参数)
示例: 使用后缀法 Math.abs
求绝对值。
参考代码
scala> Math.abs(-1) res10: Int = 1 scala> Math.abs(-3) res11: Int = 3
1.9.2 中缀调用法
语法
对象名 方法名 参数 1 to 10 //如果有多个参数,使用括号括起来
示例: 使用中缀法 Math.abs
求绝对值。
scala> Math abs -3 res12: Int = 3
1.9.3 操作符即方法
来看一个表达式:
1 + 1
大家觉得上面的表达式像不像方法调用?在Scala 中,+ - * / % 等这些操作符和 Java 一样,但在 Scala 中,所有的操作符都是方法,操作符是一个方法名字是符号的方法。
1.9.4 花括号调用法
语法格式如下:
Math.abs{ // 表达式1 // 表达式2 }
方法只有一个参数,才能使用花括号调用法,因为块表达式只有一个返回值,所以只能给1个参数用。示例: 使用花括号调用法 Math.abs
求绝对值。参考代码如下:
scala> Math.abs{-10} res13: Int = 10
1.9.5 无括号调用法
如果方法没有参数,可以省略方法名后面的括号。示例如下:
- 定义一个无参数的方法,打印 “hello amo”。
- 使用无括号调用法调用该方法。
参考代码:
scala> def m3()=println("hello amo") m3: ()Unit scala> m3() hello amo
1.10 函数 – 重点掌握
Scala 支持函数式编程,编写 Spark/Flink 程序中,会大量使用到函数。函数和我们的对象一样, 在 Scala 中都是属于一等公民。
1.10.1 定义函数
简便语法:
val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体
函数是一个 对象(变量)。 类似于方法,函数也有输入参数和返回值,函数定义不需要使用 def
定义,无需指定返回值类型。
示例:
- 定义一个两个数值相加的函数。
- 调用该函数。
参考代码:
scala> val add = (x:Int, y:Int) => x + y add: (Int, Int) => Int = <function2> scala> add(1,2) res3: Int = 3
1.10.2 方法和函数的区别
方法是隶属于类或者对象的,在运行时,它是加载到 JVM 的方法区中。但是函数本身就是一个对象,可以将函数对象赋值给一个变量,在运行时,它是加载到 JVM 的堆内存中,函数是一个对象,继承自 FunctionN,函数对象有 apply,curried,toString,tupled 这些方法,方法则没有。通俗来说,从定义的方式就可以看出方法和函数的区别:
- 一般我们使用 def 定义的为方法。
- 而通过 val 或者 var 定义一个对象来接收的,一般就是函数。
- 因为方法无法赋值给对象,但是函数定义的时候,就是直接赋值给了对象的。
示例: 方法无法赋值给变量。
scala> def add(x:Int, y:Int)=x+y add: (x: Int, y: Int)Int scala> val a = add <console>:18: error: missing argument list for method add Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`. val a = add ^ scala>
1.10.3 方法转换为函数
有时候需要将方法转换为函数,作为变量传递,就需要将方法转换为函数。使用 _
即可将方法转换为函数。
示例:
- 定义一个方法用来进行两个数相加。
- 将该方法转换为一个函数,赋值给变量。
参考代码:
scala> def add(x:Int,y:Int)=x+y add: (x: Int, y: Int)Int scala> val a = add _ a: (Int, Int) => Int = $Lambda$1375/1645141562@34696884
不用过多去思考为何加了一个 _
就能转换成函数,这里没有什么特殊的意思,就是语法要求而已。
1.10.4 完全体函数
前面写的函数,实际上是一种省略了返回值的写法,完全体如下:
val func: (Int, Int) => Int = (x, y) => x + y
其中:前面的 (Int, Int) => Int 是对象 func(也就是这个函数)的类型,类型是一个函数,函数的内容格式是两个 int 参数输入,返回一个 int 输出,后面的 (x, y) => x + y 就是定义形参,给形参命名,同时写上函数体,一般我们不写完全体,简略体就够了。容易混淆的错误写法:
scala> val addFunc: Int = (x:Int, y:Int) => x + y <console>:17: error: type mismatch; found : (Int, Int) => Int required: Int val addFunc: Int = (x:Int, y:Int) => x + y
对函数 addFunc 的类型定义错误,addFunc 是一个函数,函数的类型必须是能看出什么输入 + 得到什么输出,也就是这里 addFunc 的类型应该是 (Int, Int) => Int 也就是,两个 Int 输入参数,得到一个 Int 输出返回。
1.11 数组 – 重点掌握
Scala 中数组的概念是和 Java 类似,可以用数组来存放一组数据。Scala 中,有两种数组,一种是 定长数组,另一种是 变长数组。
1.11.1 定长数组
- 定长数组指的是数组的 长度 是 不允许改变 的。
- 数组的 元素 是 可以改变 的。
java 中数组是根据类型来定义的比如 int[] String[],在 Scala 中, 数组也是一个类,Array 类,存放的内容通过泛型来定义,类似 java 中 List 的定义,语法格式如下:
// 通过指定长度定义数组 val/var 变量名 = new Array[元素类型](数组长度) // 用元素直接初始化数组 val/var 变量名 = Array(元素1, 元素2, 元素3...)
在 Scala 中,数组的泛型使用 []
来指定(java <>),使用 ()
来获取元素(java []),这两点要注意,不要混淆了。
示例1:
- 定义一个长度为 100 的整型数组。
- 设置第 1 个元素为 110。
- 打印第 1 个元素。
参考代码:
scala> val a = new Array[Int](100) scala> a(0) = 110 scala> println(a(0)) 110
示例2:
- 定义一个包含以下元素的数组: java、scala、python。
- 获取数组长度。
参考代码:
// 定义包含jave、scala、python三个元素的数组 scala> val a = Array("java","scala","python") a: Array[String] = Array(java, scala, python) scala> a.length res18: Int = 3
为什么带初值的就不用 new 呢,这里会用到一个 apply 方法,后面会详细说。现在只需要知道,直接 Array(1, 2, 3) 来创建数组,其实就是自动调用了 Array 类中的 apply 方法,apply 方法做的事情就是,new array(3) 然后把 3 个元素放进去,也就是这些工作自动帮我们做了。
1.11.2 变长数组
变长数组指的是数组的长度是可变的,可以往数组中添加、删除元素。定义变长数组,创建变长数组,需要提前导入 ArrayBuffer 类 import scala.collection.mutable.ArrayBuffer
语法:
创建空的 ArrayBuffer 变长数组,语法结构:
val/var a = ArrayBuffer[元素类型]()
创建带有初始元素的 ArrayBuffer
val/var a = ArrayBuffer(元素1,元素2,元素3....)
示例1: 定义一个长度为0的整型变长数组
参考代码:
val a = ArrayBuffer[Int]()
为什么长度为 0 的就必须加泛型呢? 如果不加就是下面这样:
因为没有初值来判断泛型, 所以泛型是 Nothing,同时不要忘记省略(),不要用方法调用的概念认为不带参数就可以不写括号了, 这样是不可以的,如下:
就像前面提过一句的 apply 方法,如果不带(),那么就不会调用 apply 方法,不调用 apply 方法,那么就没有 ArrayBuffer 的调用,也就是不知道调用谁了。
示例2: 定义一个包含以下元素的变长数组。
"hadoop", "storm", "spark"
参考代码
scala> var a2 = ArrayBuffer("hadoop", "storm", "spark") a2: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(hadoop, storm, spark)
添加/修改/删除元素。 使用 +=
添加元素、使用 -=
删除元素、使用 ++=
追加一个数组到变长数组。
示例:
- 定义一个变长数组,包含以下元素: “hadoop”、“spark”、“flink”。
- 往该变长数组添加一个 “flume” 元素。
- 从该变长数组删除 “hadoop” 元素。
- 再将一个数组,该数组包含 “hive”, “sqoop” 追加到变长数组中。
参考代码
scala> import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer scala> val a = ArrayBuffer("hadoop", "spark", "flink") a: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(hadoop, spark, flink) scala> a += "flume" res19: a.type = ArrayBuffer(hadoop, spark, flink, flume) scala> a -= "hadoop" res20: a.type = ArrayBuffer(spark, flink, flume) scala> val a2 = ArrayBuffer("hive", "sqoop") a2: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(hive, sqoop) scala> a ++= a2 res21: a.type = ArrayBuffer(spark, flink, flume, hive, sqoop)
1.11.3 遍历数组
可以使用以下两种方式来遍历数组:
- 使用
for表达式
直接遍历数组中的元素。 - 使用
索引
遍历数组中的元素。
示例1:
- 定义一个数组,包含以下元素 1,2,3,4,5。
- 使用 for 表达式直接遍历,并打印数组的元素。
参考代码
scala> val a = Array(1,2,3,4,5) a: Array[Int] = Array(1, 2, 3, 4, 5) scala> for(i <- a) println(i) 1 2 3 4 5
示例2:
- 定义一个数组,包含以下元素1,2,3,4,5。
- 使用 for 表达式基于索引下标遍历,并打印数组的元素。
参考代码
scala> val a = Array(1,2,3,4,5) a: Array[Int] = Array(1, 2, 3, 4, 5) scala> for(i <- 0 until a.length) print(a(i)) 12345 scala> for(i <- 0 to a.length-1) print(a(i)) 12345
0 until n——生成一系列的数字,包含0,不包含n,0 to n ——包含0,也包含n。
1.11.4 数组常用算法
Scala 中的数组封装了一些常用的计算操作,将来在对数据处理的时候,不需要我们自己再重新实现。以下为常用的几个算法:求和——sum方法、求最大值——max方法、求最小值——min方法、排序——sorted方法
求和: 数组中的 sum
方法可以将所有的元素进行累加,然后得到结果。
示例:
- 定义一个数组,包含以下几个元素(1,2,3,4)。
- 请计算该数组的和。
参考代码:
scala> val a = Array(1,2,3,4) a: Array[Int] = Array(1, 2, 3, 4) scala> a.sum res26: Int = 10
最大值: 数组中的 max
方法,可以获取到数组中的最大的那个元素值。
示例:
- 定义一个数组,包含以下几个元素(4,1,2,4,10)。
- 获取数组的最大值。
参考代码:
scala> val a = Array(4,1,2,4,10) a: Array[Int] = Array(4, 1, 2, 4, 10) scala> a.max res27: Int = 10 scala> a.min res28: Int = 1 scala> a.reverse res29: Array[Int] = Array(10, 4, 2, 1, 4) scala> a.sorted res30: Array[Int] = Array(1, 2, 4, 4, 10) scala> a.sorted.reverse res31: Array[Int] = Array(10, 4, 4, 2, 1)
1.12 元组 – 重点掌握
元组可以用来包含一组不同类型的值。例如:姓名,年龄,性别,出生年月。元组的元素是不可变的。数组: 同一类数据成组。元组: 不同的元素成组,并且元组内容(元素)不可变。
1.12.1 定义元组
语法
方式1: 使用括号来定义元组
val/var 元组 = (元素1, 元素2, 元素3....)
方式2:使用箭头来定义元组(元组只能有两个元素)
val/var 元组 = 元素1->元素2
示例1: 定义一个元组,包含一个学生的以下数据。
id | 姓名 | 年龄 | 地址 |
1 | amo | 18 | chengdu |
参考代码
scala> val a = (1,"amo",18,"chengdu") a: (Int, String, Int, String) = (1,amo,18,chengdu)
示例2: 定义一个元组,包含学生的姓名和年龄(paul、26)、分别使用括号、和箭头来定义元组。
参考代码:
scala> val a = ("paul", 26) a: (String, Int) = (paul,26) scala> scala> val b = "paul" -> 20 b: (String, Int) = (paul,20)
1.12.2 访问元组
使用 _1、_2、_3… 来访问元组中的元素,_1 表示访问第一个元素,依次类推。
示例: 定义一个元组,包含一个学生的姓名和性别,“crystal”, “female”,分别获取该学生的姓名和性别。
参考代码
scala> val b = "paul" -> 20 b: (String, Int) = (paul,20) scala> val a = "crystal" -> "female" a: (String, String) = (crystal,female) scala> a._1 res32: String = crystal scala> a._2 res33: String = female
1.13 列表 – List – 重点掌握
列表是 Scala 中最重要的、也是最常用的数据结构。List 具备以下性质:
- 可以保存重复的值。
- 有先后顺序。
在 Scala 中,也有两种列表,一种是不可变列表、另一种是可变列表。