大数据笔记(二十六)——Scala语言的高级特性

===================== Scala语言的高级特性 ========================
一、Scala的集合
1、可变集合mutable

不可变集合immutable

//不可变集合
val math = scala.collection.immutable.Map("Alice"->80,"Bob"->90)
//可变集合
val chinese = scala.collection.mutable.Map("Alice"->80,"Bob"->90)
//1. 获取集合中的值
chinese("Bob")
if(chinese.contains("Bob")){
chinese("Bob")
}else{
-1
}
//简写
chinese.getOrElse("Bob",-1)
//2、修改更新可变集合中的值
chinese("Bob")=100
//添加新的元素
chinese += "Jerry" ->90
//删除元素
chinese -= "Bob"
//迭代
for (s <- chinese) println(s)
chinese.foreach(println)

2、列表:List

import scala.collection.mutable

//可变 不可变列表
//不可变:List var nameList = List("Mary","Tom","Mike")
var intList = List(1,2,3,4)
var nullList:List[Nothing] = List()
//二维
var din:List[List[Int]] = List(List(1,2,3),List(4,5,6))
println("第一个人的名字: "+nameList.head)
println("除去第一个: "+nameList.tail) //可变列表(LinkedList)
val myList = mutable.LinkedList(1,2,3,4,5,6)
//每个元素乘2
//类似PLSQL:cursor 游标
var cur = myList
//当指针!=null时 Nil:Scala的null值
while (cur != Nil){
//对当前值*2
cur.elem = cur.elem*2
//指向下一个值
cur = cur.next } //结果
println(myList) //LinkedList(2, 4, 6, 8, 10, 12)

3、序列: 常用的序列: Vector、Range

//序列:Vector,Range
//Vector:带下标的序列
val v = Vector(1,2,3,4,5,6)
//返回第一个满足条件的元素
v.find(_> 3)
//更新下标为2
v.updated(2,100) //Range:整数序列
println("第一种写法: "+Range(0,5))
println("第二种写法: "+(0 until 5))
println("第三种写法: "+(0 to 4)) ('0' to '9') ++ ('A' to 'Z') 1 to 5 toList

运行结果:

v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5, 6)

res0: Option[Int] = Some(4)

res1: scala.collection.immutable.Vector[Int] = Vector(1, 2, 100, 4, 5, 6)

第一种写法: Range(0, 1, 2, 3, 4)
res2: Unit = ()
第二种写法: Range(0, 1, 2, 3, 4)
res3: Unit = ()
第三种写法: Range(0, 1, 2, 3, 4)
res4: Unit = () res5: scala.collection.immutable.IndexedSeq[Char] = Vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z) res6: List[Int] = List(1, 2, 3, 4, 5)

4、集(Set): 是不重复元素的集合,默认的实现:HashSet

import scala.collection.mutable

//集(Set):不重复的元素集合,默认实现:HashSet
var s1 = Set(10,2,0,1)
s1 += 0
s1
s1 += 100
s1
var weeksDay = mutable.LinkedHashSet("星期一","星期二")
weeksDay + "星期五"
weeksDay.contains("星期二")
var s2 = mutable.SortedSet(1,2,3,100,10,4) //判断一个Set是否是另一个Set的
Set("星期一","星期二").subsetOf(weeksDay)
var set1 = Set(1,2,3,4,5,6)
var set2 = Set(5,6,7,8,9,10)
//并交差
set1 union set2
set1 intersect set2
set1 diff set2

运行结果:

import scala.collection.mutable

s1: scala.collection.immutable.Set[Int] = Set(10, 2, 0, 1)
res0: Unit = ()
res1: scala.collection.immutable.Set[Int] = Set(10, 2, 0, 1)
res2: Unit = ()
res3: scala.collection.immutable.Set[Int] = Set(0, 10, 1, 2, 100)
weeksDay: scala.collection.mutable.LinkedHashSet[String] = Set(星期一, 星期二)
res4: scala.collection.mutable.LinkedHashSet[String] = Set(星期一, 星期二, 星期五)
res5: Boolean = true
s2: scala.collection.mutable.SortedSet[Int] = TreeSet(1, 2, 3, 4, 10, 100) res6: Boolean = true
set1: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)
set2: scala.collection.immutable.Set[Int] = Set(5, 10, 6, 9, 7, 8) res7: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 6, 9, 2, 7, 3, 8, 4)
res8: scala.collection.immutable.Set[Int] = Set(5, 6)
res9: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)

5、模式匹配:match

相当于Java的 instanceof、switch case
(*)Scala的一个强大的switch case语句
(*)类型的检查

//模式匹配
var ch1 = '-'
var flag = 0
ch1 match {
case '+' => flag = 1
case '-' => flag = -1
case _ => flag = 0
}
println(flag) //2.Scala的守卫:匹配某种类型的所有值
var ch2 = '6'
var digit:Int = -1
ch2 match {
case '+' => println("+")
case '-' => println("-") }

运行结果:

ch1: Char = -
flag: Int = 0
res0: Unit = () -1
res1: Unit = () ch2: Char = 6
digit: Int = -1
scala.MatchError: 6 (of class java.lang.Character)
at #worksheet#.#worksheet#(Demo7.sc0.tmp:8)

6、样本类:case class

好处:支持模式匹配
使用样本类来定义DataFrame(表)的Schema(表的结构) -----> Spark SQL

//使用样本类支持模式匹配: 类似: instanceof

class Vehicle

case class Car(name:String) extends Vehicle
case class Bike(name:String) extends Vehicle //定义一个Car对象
var aCar:Vehicle = new Car("这是汽车")
aCar match {
case Car(name) => println("这是一辆汽车")
case Bike(name)=> println("这是一辆自行车")
case _ => println("其他类型")
}

运行结果:这是一辆汽车

二、Scala的泛型: T ----> 重要
1、泛型类: [T]

package main.scala

/**
* Created by YOGA on 2018/2/5.
*/
class GenericClass[T]{
private var content:T = _
def set(value:T) = {
content = value
}
def get():T = {content}
}
object GenericClass {
def main(args: Array[String]): Unit = {
//Int类型
var intGeneric = new GenericClass[Int]
intGeneric.set(123)
println("得到的值: "+intGeneric.get())
//String类型
var stringGeneric = new GenericClass[String]
stringGeneric.set("Hello")
println("得到的值:"+stringGeneric.get())
}
}

运行结果:

得到的值: 123
得到的值:Hello

2、泛型函数
创建一个函数,功能:创建一个Int类型的数组

*代表可变参数

def mkIntArray(elems:Int*) = Array[Int](elems:_*)
mkIntArray(1,2,3,4,5)

创建一个函数,功能:创建一个String类型的数组

def mkStringArray(elems:String*) = Array[String](elems:_*)
mkStringArray("Tom","Mary","Mike")

问题:能否创建一个通用的函数,既能创建Int类型数组,也能创建String类型的数组
在泛型函数中:T有要求:必须是ClassTag类型

import scala.reflect.ClassTag
def mkArray[T:ClassTag](elems:T*) = Array[T](elems:_*)
mkArray(1,2,3,4,5)
mkArray("Tom","Mary","Mike")

ClassTag: 表示执行Scala程序时的运行信息,该例子中,表示数据的类型

3、上界和下界:规定泛型的取值范围
举例:
(1) 普通数据类型
int x 范围
100 <= x <= 200
下界 上界

(2)规定:泛型的取值范围
继承关系
A ---> B ---> C ---> D

D <: y泛型 <: B ====> 表示:y的类型只能是:B C D

概念
上界:定义 S <: T,表示S的类型必须是T的子类
下界:定义 U >: T 表示U的类型必须是T的父类

package main.scala
//上界
//父类
class Vehicle2{
def drive() = {println("Driving")}
}
//子类
class Car2 extends Vehicle2{
override def drive():Unit = {println("Car Driving")}
}
class Bike2 extends Vehicle2{
override def drive():Unit = {println("Bike Driving")}
}
object ScalaUpperBound {
//泛型
def takeVehicle[T <: Vehicle2](v:T) = {v.drive()} def main(args: Array[String]) {
var v1:Vehicle2 = new Vehicle2
takeVehicle(v1)
var v2:Car2 = new Car2
takeVehicle(v2) }
}

运行:

Driving
Car Driving

4、视图界定:是上界和下界的一个扩展
概念:适用范围更广泛
以上界为例 <:
接收除了所有的子类,还允许接收隐式转换过去的类型
定义:<%

看个例子: 定义一个函数完成两个字符串的拼加

def addTwoString[T <: String](x:T,y:T) = {println(x+"****"+y)}

调用:

addTwoString("Hello","World")

再调用一次:会报错

 addTwoString(100,200) ====> 100*****200

使用视图界定重新来定义

def addTwoString[T <% String](x:T,y:T) = {println(x+"****"+y)}

表示T的类型:
(1)String和String的子类
(2)还可以是可以转换成String的类型

重新调用:

addTwoString(100,200)
error: No implicit view available from Int => String.

视图界定的时候:一定有一个转换规则(隐式转换函数)

implicit def int2String(n:Int):String = {n.toString}
addTwoString(100,200)

执行过程: (1) 调用隐式转换函数int2String: int ====> String
100 ---> "100"
200 ---> "200"
(2) 再调用addTwoString

5、协变、逆变:看成是视图界定的一个扩展

  协变:一个泛型类接收的泛型参数的值可以是本身的类型或者是子类的类型

逆变:一个泛型类接收的泛型参数的值可以是本身的类型或者是父类的类型

package main.scala.demo1

//Scala的协变: 一个泛型类接收的泛型参数的值可以是本身的类型或者是子类的类型
// 在泛型前 + //动物
class Animal{} //子类:鸟
class Bird extends Animal //子类:麻雀
class Sparrow extends Bird //类:吃东西(动作)
class EatSomething[+T](t:T){} object DemoClass1 { def main(args: Array[String]): Unit = {
//创建一个鸟吃东西的对象
var c1:EatSomething[Bird] = new EatSomething[Bird](new Bird) //创建一个动物吃东西的对象
//var c2:EatSomething[Animal] = new EatSomething[Animal](new Animal)
//问题:能否将c1付给c2?
//原因:尽管Bird是Animal的子类,但是EatSomething[Bird]不是EatSomething[Animal]的子类
//方法:把EatSomething[Bird] 变成 EatSomething[Animal] 子类 方法是添加一个泛型类 class = EatSomething[+T](t:T){}
var c2:EatSomething[Animal] = c1 //再举个例子
var c3:EatSomething[Sparrow] = new EatSomething[Sparrow](new Sparrow)
var c4:EatSomething[Animal] = c3
}
}
package main.scala.demo1

//Scala的逆变: 一个泛型类接收的泛型参数的值可以是本身的类型或者是父类的类型
// 语法: -号 //动物
class Animal{} //子类:鸟
class Bird extends Animal //子类:麻雀
class Sparrow extends Bird //定义吃东西的类
class EatSomething[-T](t:T){} object DemoClass2 {
def main(args: Array[String]): Unit = {
//创建一个鸟吃东西的对象
var c1:EatSomething[Bird] = new EatSomething[Bird](new Bird) //创建一个麻雀吃东西的对象
//var c2:EatSomething[Sparrow] = new EatSomething[Sparrow](new Sparrow)
//问题:能否将c1 付给c2?
//原因:尽管Bird是Sparrow的父类,但是EatSomething[Bird]不是EatSomething[Sparrow]的父类
//接收参数:Bird 是Sparrow的父类
var c2:EatSomething[Sparrow] = c1
}
}

6、隐式转换函数:implicit

package main.scala.demo3
//隐式转换
//水果
class Fruit(name:String){
def getFruitName():String = {name}
} //猴子
class Monkey(f:Fruit){
def say() = {println("Monkey like" + f.getFruitName())}
} object ImplicitDemo { //定义一个隐式转换规则:Fruit对象 ===》Monkey对象
implicit def fruit2Monkey(f:Fruit):Monkey = {new Monkey(f)} def main(args: Array[String]) {
//创建一个水果的对象
var f:Fruit = new Fruit("香蕉") //希望直接调用f.say()方法
//将Fruit的对象转换成Monkey的对象
f.say()
}
}

运行:

Monkey like香蕉

7、隐式参数:参数前面加一个implicit关键字

//Scalad的隐式参数
def testParam(implicit name:String) = {println("The value is "+name)}
implicit val name:String = "这是一个隐式参数" //调用:不想传递参数
testParam //找到两个值中小的那个
def smaller[T](a:T,b:T)(implicit order:T => Ordered[T]) = if(a<b)
a else b
smaller(1,3)
smaller("Hello","abc")

运行结果:

testParam: testParam[](implicit val name: String) => Unit
name: String = 这是一个隐式参数 The value is 这是一个隐式参数
res0: Unit = () smaller: smaller[T](val a: T,val b: T)(implicit val order: T => Ordered[T]) => T res1: Int = 1
res2: String = Hello

8、隐式类:类前面加一个implicit关键字

package main.scala.demo4

/*
隐式类的功能:对类的功能进行加强
顺序:
第一步:先调用隐式转换类,把 1 ===> Calc对象
第二步:调用Calc对象的add方法
*/
object ImplicitDemo4 { //定义一个隐式类
implicit class Calc(x:Int){
def add(y:Int) = x + y
} def main(args: Array[String]): Unit = {
//进行两个数的相加 1.add(2)
println("两个数字的和是:" + 1.add(2))
}
}

运行:

两个数字的和是:3 
上一篇:深入浅出Redis(二)高级特性:事务


下一篇:Python:Day51 web框架