Scala语言简介
Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序。它也能运行于CLDC配置的Java ME中。目前还有另一.NET平台的实现,不过该版本更新有些滞后。Scala的编译模型(独立编译,动态类加载)与Java和C#一样,所以Scala代码可以调用Java类库(对于.NET实现则可调用.NET类库)。Scala包括编译器和类库,以及BSD许可证发布。
学习Scala编程语言,为后续学习Spark奠定基础。
Scala数据类型
① 数值类型:Byte,Short,Int,Long,Float,Double
Byte: 8位有符号数字,从-128 到 127
Short: 16位有符号数据,从-32768 到 32767
Int: 32位有符号数据
Long: 64位有符号数据
例如:
val a:Byte = 10
a+10
得到:res9: Int = 20
这里的res9是新生成变量的名字
val b:Short = 20
a+b
**注意:**在Scala中,定义变量可以不指定类型,因为Scala会进行类型的自动推导。
② 字符类型和字符串类型:Char和String
对于字符串,在Scala中可以进行插值操作
③ Unit类型:相当于Java中的void类型
④Nothing类型:一般表示在执行过程中,产生了Exception
Scala语法基础
一、循环语句
1、类似Java:for、while、do…while
2、使用foreach进行迭代(循环)
val list = List("Mary", "Tom", "Mike")
//for的第一种写法
for (elem <- list) {
println(elem)
}
System.out.println("************************")
// 第二种写法
// 加上判断条件,打印名字长度大于3的
for (elem <- list if elem.length() > 3) {
println(elem)
}
System.out.println("************************")
// 第三种写法,使用yield关键字,产生一个新的集合
// 把list中的每个元素变成大写
val newList: List[String] = for{
elem <- list
s = elem.toUpperCase
}yield (s)
for (elem <- newList) {
println(elem)
}
System.out.println("************************")
// 使用while
var i = 0
while (i < list.length){
println(list(i))
i += 1
}
System.out.println("************************")
var j = 0
do {
println(list(j))
j += 1
}while(j < list.length)
System.out.println("************************")
// 使用forEach进行迭代
// foreach和map的区别:foreach没有返回值,map有返回值
list.foreach(println)
二、Scala的函数参数
1、函数参数的求值策略
(1)call by value 定义: :
对函数实参求值,并且只求一次
(2)call by name 定义: :=>
函数的实参在函数体内部每次用到的时候,都会被求值
(3)复杂点的例子
5-05
x是call by value, y是call by name
def bar(x:Int,y: => Int):Int = 1
定义一个死循环
def loop():Int = loop
调用bar函数,哪次会产生死循环?
1、bar(1,loop) 正常返回1
2、bar(loop,1) 会产生死循环
分析原因!!
2、Scala中的函数参数的类型
(1)默认参数
def func1(name:String="Tom"):String = "Hello " + name
(2)代名参数:应用的场景:当有多个默认参数的时候,通过代名参数可以确定是给哪一个参数赋值
def func2(str:String="Good Morning ",name:String="Tom ",age:Int=20)
=
str + name + " and the age of " + name + " is " + age
调用一下
func2()
func2(name="abcd") 把abcd付给name参数
(3)可变参数
scala> //可变参数
scala> //求多个数字的和
scala> def sum(args:Int*) = {
| var result = 0
| for(s <- args) result += s
| result
| }
sum: (args: Int*)Int
scala> //调用
scala> sum(1,2,3)
res4: Int = 6
scala> sum(1,2,3,4,5)
res5: Int = 15
三、懒值(lazy值):常量如果是lazy的,他的初始化就会被推迟,推迟到第一次使用该常量的时候
做一点铺垫:Spark的核心是RDD(数据集合),操作数据集合中的数据,需要使用算子(函数、方法)
算子有两种
1、Transformation:延时加载,不会触发计算
2、Action:会触发计算
scala> //lazy值 懒值
scala> val x:Int = 10
x: Int = 10
scala> val y:Int = x + 1
y: Int = 11
scala> //上面的特点是立即执行计算
scala> lazy val z:Int = x + 1
z: Int = <lazy>
scala> //常量z的初始化就会被推迟
scala> //没 有发计算
scala> z
res6: Int = 11
再举一个例子:读文件
(1)存在的文件
(2)不存在的文件
scala> val words = scala.io.Source.fromFile("d:\\temp\\student.txt").mkString
words: String =
1 Tom 23
2 Mary 25
3 Mike 24
5 Jone 20
6 Jerry 21
scala> lazy val words = scala.io.Source.fromFile("d:\\temp\\student.txt").mkString
words: String = <lazy>
scala> words
res7: String =
1 Tom 23
2 Mary 25
3 Mike 24
5 Jone 20
6 Jerry 21
scala> //读取一个不存在的文件
scala> lazy val words = scala.io.Source.fromFile("d:\\temp\\aaa.txt").mkString
words: String = <lazy>
scala> //不会产生Exception
scala> words
java.io.FileNotFoundException: d:\temp\aaa.txt (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at scala.io.Source$.fromFile(Source.scala:91)
at scala.io.Source$.fromFile(Source.scala:76)
at scala.io.Source$.fromFile(Source.scala:54)
at .words$lzycompute(<console>:11)
at .words(<console>:11)
... 32 elided
scala>
四、简介:例外:Exception
类似Java:try…catch…finally
五、数组
1、数组的类型
(1)定长数组:Array
scala> // 定长数组: Array
scala> val a = new Array[Int](10)
a: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> val b = new Array[String](5)
b: Array[String] = Array(null, null, null, null, null)
scala> val c = Array("Tom","Mary","Mike")
c: Array[String] = Array(Tom, Mary, Mike)
(2)变长数组:ArrayBuffer
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
scala> val d = ArrayBuffer[Int]()
d: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
scala> //往变长数组中添加元素
scala> d += 1
res9: d.type = ArrayBuffer(1)
scala> d += 2
res10: d.type = ArrayBuffer(1, 2)
scala> d += 3
res11: d.type = ArrayBuffer(1, 2, 3)
scala> //一次添加多个元素
scala> d += (10,20,30)
res12: d.type = ArrayBuffer(1, 2, 3, 10, 20, 30)
去掉最后的两个元素
d.trimEnd(2)
scala> //遍历数组
scala> var a = Array("Tom","Mary","Mike")
a: Array[String] = Array(Tom, Mary, Mike)
scala> //使用for语句进行遍历
scala> for( s<- a) println(s)
Tom
Mary
Mike
scala> //使用foreach
scala> a.foreach(print)
TomMaryMike
解释一下:
myArray1.sortWith(_ > _)
写完整
myArray1.sortWith((a,b)=>{if(a>b) true else false})
匿名函数:(a,b)=>{if(a>b) true else false} ----> 简写:_ > _
把匿名函数作为了sortWith参数值 -----> 把sortWith叫做高阶函数(Scala中的函数式编程)
2、多维数组:和Java一样,通过数组的数组来实现
下表中为 Scala 语言中处理数组的重要方法,使用它前我们需要使用import Array._
引入包。
序号 | 方法和描述 |
---|---|
1 | def apply( x: T, xs: T* ): Array[T]创建指定对象 T 的数组, T 的值可以是 Unit, Double, Float, Long, Int, Char, Short, Byte, Boolean。 |
2 | def concat[T]( xss: Array[T]* ): Array[T]合并数组 |
3 | def copy( src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int ): Unit复制一个数组到另一个数组上。相等于 Java’s System.arraycopy(src, srcPos, dest, destPos, length)。 |
4 | def empty[T]: Array[T]返回长度为 0 的数组 |
5 | def iterate[T]( start: T, len: Int )( f: (T) => T ): Array[T]返回指定长度数组,每个数组元素为指定函数的返回值。以上实例数组初始值为 0,长度为 3,计算函数为a=>a+1scala>Array.iterate(0,3(a=>a+1)res1: Array[Int] = Array(0, 1, 2) |
6 | def fill[T]( n: Int )(elem: => T): Array[T]返回数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。 |
7 | def fill[T]( n1: Int, n2: Int )( elem: => T ): Array[Array[T]]返回二数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。 |
8 | def ofDim[T]( n1: Int ): Array[T]创建指定长度的数组 |
9 | def ofDim[T]( n1: Int, n2: Int ): Array[Array[T]]创建二维数组 |
10 | def ofDim[T]( n1: Int, n2: Int, n3: Int ): Array[Array[Array[T]]]创建三维数组 |
11 | def range( start: Int, end: Int, step: Int ): Array[Int]创建指定区间内的数组,step 为每个元素间的步长 |
12 | def range( start: Int, end: Int ): Array[Int]创建指定区间内的数组 |
13 | def tabulate[T]( n: Int )(f: (Int)=> T): Array[T]返回指定长度数组,每个数组元素为指定函数的返回值,默认从 0 开始。以上实例返回 3 个元素:scala> Array.tabulate(3)(a => a + 5)res0: Array[Int] = Array(5, 6, 7) |
14 | def tabulate[T]( n1: Int, n2: Int )( f: (Int, Int ) => T): Array[Array[T]]返回指定长度的二维数组,每个数组元素为指定函数的返回值,默认从 0 开始。 |
六、映射:就是的对,用Map来表示
scala> //创建一个Map(映射)
scala> //学生的成绩
scala> val scores = Map("Tom"->80,"Mary"->85,"Mike"->82)
scores: scala.collection.immutable.Map[String,Int] = Map(Tom -> 80, Mary -> 85, Mike -> 82)
scala> //Scala中,映射有两种 1、不可变的Map 2、可变的Map
scala> //定义一个可变的Map
scala> //保存学生的语文成绩
scala> //有不同写法
scala> val chinese = scala.collection.mutable.Map(("Alice",80),("Bob",95),("Mary",75))
chinese: scala.collection.mutable.Map[String,Int] = Map(Bob -> 95, Alice -> 80, Mary -> 75)
scala> //映射的操作
scala> //1、获取映射中的值
scala> chinese("Bob")
res19: Int = 95
scala> chinese.get("Bob")
res20: Option[Int] = Some(95)
scala> //2、获取一个不存在的值
scala> chinese("Tom")
java.util.NoSuchElementException: key not found: Tom
at scala.collection.MapLike$class.default(MapLike.scala:228)
at scala.collection.AbstractMap.default(Map.scala:59)
at scala.collection.mutable.HashMap.apply(HashMap.scala:65)
... 32 elided
scala> //先判断一下 ,key是否存在
scala> if(chinese.contains("Tom")){
| chinese("Tom")
| }else{
| -1
| }
res22: Int = -1
scala> //可以简写
scala> chinese.getOrElse("Tom",-1)
res23: Int = -1
scala> chinese.getOrElse("Alice",-1)
res24: Int = 80
scala> //更新映射中的值:必须是可变的映射
scala> chinese("Bob")
res25: Int = 95
scala> chinese("Bob") = 100
scala> chinese("Bob")
res27: Int = 100
scala> //如何进行迭代?类似数组
scala> //可以使用for ,foreach
scala> for(s <- chinese) println(s)
(Bob,100)
(Alice,80)
(Mary,75)
scala> chinese.foreach(println)
(Bob,100)
(Alice,80)
(Mary,75)
七、元组:Tuple
1、复习一下:Tuple在Pig中、在Storm中都用到了
2、Scala的Tuple:是不同类型的值的集合
scala> //Tuple3代表的是有三个元素
scala> val t2 = Tuple2(2,"World")
t2: (Int, String) = (2,World)
scala> //访问Tuple中的元素
scala> t1.
_1 _3 copy hashCode productArity productIterator toString zipped
_2 canEqual equals invert productElement productPrefix x
scala> t1._1
res30: Int = 1
scala> t1._2
res31: Double = 3.14
scala> t1._3
res32: String = Hello
scala> //如何遍历Tuple中的元素
scala> //首先使用productIterator生成再遍历
scala> t1.productIterator.foreach(println)
1
3.14
Hello
**注意:**要遍历Tuple中的元素,需要首先生成对应的迭代器。不能直接使用for或者foreach。
Scala的面向对象:类似Java
一、面向对象的基本概念
1、封装:类class
2、继承
3、多态
(1)当定义属性是private时候,scala会自动为其生成对应的get和set方法
(2) 如果只希望get方法,不生成set方法,可以将属性定义为val常量
(3)如果希望不能直接被外部类访问(即希望不要生成get和set方法),可以使用private[this]修饰类或者属性
二、定义类:关键字class
举例:创建一个学生类
三、内部类
四、类的构造器:两种
1、主构造器:和类的声明在一起,并且一个类只有一个主构造器
2、辅助构造器:一个类可以有多个辅助构造器,通过关键字this
五、Object对象:相当于Java中的static
1、Object中的内容都是静态的
2、Scala中,没有静态修饰符static
3、如果class的名字,跟object的名字一样,这时候就把这个object叫做类的伴生对象
4、举例
(1)使用object实现单例模式:一个类只有一个对象
如果在Java中,构造器定义为private,提供getInstance方法
(2)使用App对象:应用程序对象
好处:可以省略main方法
六、apply方法:可以省略new关键字,根据自己的需要进行初始化
apply方法必须写在伴生对象中
scala> //创建一个数组
scala> var myarray = Array(1,2,3) ----> 使用了apply方法
myarray: Array[Int] = Array(1, 2, 3)
scala> //本质就是创建一个Array对象
scala> //省略了new关键字
七、继承:
1、跟Java一样,使用关键字extends
2、抽象类、抽象字段
八、特质(trait):类似Java中的抽象类,并且支持多重继承
本质:就是抽象类,特点:支持多重继承
九、包和包对象:
包可以包含类、对象和特质,但不能包含函数或者变量的定义。很不幸,这是Java虚拟机的局限。
把工具函数或者常量添加到包而不是某个Utils对象,这是更加合理的做法。Scala中,包对象的出现正是为了解决这个局限。
Scala中的包对象:常量,变量,方法,类,对象,trait(特质),包
十、scala中文件访问
Scala的函数式编程:Scala是一个多范式的编程语言
举例:使用Spark执行WordCount
sc是Spark的一个对象,SparkContext这个对象非常重要
sc.textFile("hdfs://bigdata111:9000/input/data.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
一、回顾:Scala中的函数:是头等公民
关键字:def
二、匿名函数:没有名字的函数
scala> //定义一个匿名函数
scala> (x:Int) => x*3
res1: Int => Int = <function1>
scala> //调用匿名函数
scala> //定义一个 数组,把数组中的每个元素乘以3
scala> //Array(1,2,3) 把Array中的每个元素作为x参数值,传递给匿名函数
scala> Array(1,2,3).map((x:Int) => x*3)
res2: Array[Int] = Array(3, 6, 9)
scala> //把(x:Int) => x*3 作为了map的参数值
三、高阶函数(带有函数参数的函数):把一个函数作为另一个函数的参数值
四、高阶函数示例
(*)map:相当于一个循环,对某个集合中的每个元素进行操作(就是接受一个函数),返回一个新的集合
val numbers = List(1,2,3,4,5,6,7,8,9,10)
numbers.map((i:Int)=>i*2)
简写 numbers.map(_ * 2)
(*)foreach:相当于一个循环,对某个集合中的每个元素进行操作(就是接受一个函数),不返回结果
numbers.foreach((i:Int)=>i*2)
(*)filter: 过滤,选择满足条件的数据
查询能够被2整除的数字
numbers.filter((i:Int)=> i%2==0) 如果是true,就返回结果
(*)zip: 合并集合
List(1,2,3).zip(List(4,5,6))
(*)partition: 根据断言(就是条件,通过一个匿名函数来实现)的结果,来进行分区
举例:把能够被2整除的分成一个区,不能整除的分成另一个区
numbers.partition((i:Int)=> i%2==0)
(*)find: 查找第一个满足条件(断言)的元素
查询第一个能够被3整除的数字
numbers.find(_%3 == 0)
(*)flatten:把嵌套的结构展开
List(List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9)).flatten
(*)flatMap 相当于 map + flatten
var myList = List(List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9))
myList.flatMap(x=> x.map(_*2))
结果 res16: List[Int] = List(4, 8, 12, 16, 20, 2, 6, 10, 14, 18)
过程 (1)将List(2, 4, 6, 8, 10)和List(1, 3, 5, 7, 9)调用x=> x.map(_*2)
(2)再合并成一个List
五、概念:闭包、柯里化
- 闭包:就是函数的嵌套,即:
在一个函数定义中,包含另外一个函数的定义;
并且在内函数中可以访问外函数中的变量。
scala> //闭包:函数的嵌套
scala> def mulBy(factor:Double)=(x:Double)=> x* factor
mulBy: (factor: Double)Double => Double
scala> //调用
scala> //1、乘以3的操作,因子=3
scala> val triple=mulBy(3)
triple: Double => Double = <function1>
scala> //值函数
scala> triple(10)
res0: Double = 30.0
scala> //2、乘以0.5的操作 factor=0.5
scala> val half = mulBy(0.5)
half: Double => Double = <function1>
scala> half(10)
res1: Double = 5.0
2. 柯里化:Currying
3. 柯里化函数(Curried Function)是把具有多个参数的函数转换为一条函数链,每个节点上是单一参数。