赞
踩
Scala是Spark的基础,我们需要研究将大数据引入到Spark的特定架构。
Scala集合很重要
由于Scala集合很多,我们在学习的时候,需要先学一个作为共性
然后在掌握差异化的部分(其实高级程序语言不是很依赖差异化的数据结构)
安装scala-2.12.10.msi
(资源位于E:\BigData\software\snd_download)
检查scala安装情况
新建一个空的Maven工程,删除main包和test包,新建一个包名为Scala,将该包mark directory as source root设置成源码包,在该包下新建一个 Scala Class中的Object类
在 Project Structure中,在Libraries 和 Global Libraries中添加 Scala SDK(每新建一个工程都要重新配置一次)
检查基本配置
package cha01
object Test01 {
def main(args: Array[String]): Unit = {
println("hello scala")
}
}
面向对象特性
函数式编程
每个函数都是一个值
支持高阶函数,柯里化(currying),样例类(case class)及模式匹配…
高阶函数:Scala中提供了大量的函数,90%的算法都无需我们自己去写,我们只需要"拼图"即可。
柯里化:多参数列表
样例类:Java类的简化版
模式匹配:将数据延伸、变形、转化的过程语法简化
Scala是静态类型语言
扩展性:隐式类、字符串插值
隐式:Scala包括隐式类、隐式变量、隐式函数(Scala中的高阶函数中包含了大量的隐式函数)
可用于将多个函数的共性逻辑声明为一个隐式函数,并将其自动传进去,可实现隐式转换
var variableName[:Type] = V
val valName[:Type] = V
type Alias = Type
type S = String
var str:S = "henry"
在Scala中所有的数据类型都是引用类型
基本类型
,并非单独的模块)
Tuple 元组
定义
可以存放不同类型的元素
一旦创建,就不能修改其中的元素
元组长度范围从1~22
表示方法
()
来表示一个元组。()
在Scala中有多重含义,可能表示:
array(int)
。method(p1, p2, ...)
。声明和初始化方法
// 简单声明和初始化
val tp2 = ("java", 88) // syntatic sugar:指那些可读性强的语法
// 明确指定元组类型
val tp2: (String, Int) = ("java", 88)
// 使用Tuple类显式声明
val tp2: Tuple2[String, Int] = Tuple2("java", 88)
元组操作
._n
来访问元组中的元素。元组的索引是从1开始的。// 获取元组中的元素
println(s"${tp2._1} ${tp2._2}")
// 对元组进行迭代
val it:Iterator[Any] = tp2.productIterator
tp2.productIterator.foreach(println) // 遍历输出元组中的所有元素
一切皆是表达式(val expr = {}):所有的构造都有值,并且可以产生结果,不需要写return
val result = if(a>b) "greater" else "lesser"
val squares = for(i <- 1 to 5) yield i*i
val result = {
val temp = a * b
temp + 10
}
惰性表达式:使用时执行(lazy val expr = {})
= += -= *= /= %=
+= - * / %(没有++和–)
> >= < <= == !=
&& || !
在Scala对象上使用运算符时,实际上是在调用一个方法。因此Scala支持运算符的重载,例如下面的例子对+
进行重载实现了复数的加法:
case class Complex(re: Double, im: Double) {
def +(that: Complex): Complex = Complex(this.re + that.re, this.im + that.im)
}
val a = Complex(1.0, 2.0)
val b = Complex(3.0, 4.0)
val c = a + b // 调用a的+方法,传入b作为参数
注意懒加载的情况
var result = if (fig) 1 else 0 // 类似三元运算符
// by 可以指定步长
val range1 = 1 to 10 by 2 // to 前闭后闭
val range2 = 1 until 10 by 3 // until 前闭后开
for(i <- 1 to 3){
print(i+" ")
}
for(i <- 1 until 3){
print(i+" ")
}
// 循环守卫 该写法不可以写成常量的形式
for(i <- 1 to 3 if i != 2){
print(i+" ")
}
// 引入变量
for(i <- 1 to 3; j=4-i){
print(j+" ")
}
// 正向遍历
for(i <- 0 until arr.length){
val item = arr(i)
}
// 反向遍历
for(i<-(0 until arr.length).reverse){
val item = arr(i)
}
// 如何实现continue的效果?-> 循环守卫
for(i <- 1 to 10 if (i != 2 && i != 3)){
print(i+" ")
}
val arr = Array(1,2,3,4,5)
//arr:Array[Int] = Array(1,2,3,4,5)
val res = for(e <- arr) yield e*2
//res:Array[Int] = Array(2,4,6,8,10)
val res1 = arr.map(_*2)
//res1:Array[Int] = Array(2,4,6,8,10)
val arr = Array(1,2,3,4,5)
val r = arr.filter(_ % 2 == 0).map(_ * 10)
println(r.toBuffer)
val it:Iterator[Any] = tp2.productIterator
it.foreach(println) // 使用了Scala的已有函数println(返回类型恰好是Unit) 1
it.foreach(e=>println(e)) // 调用自定义函数(一次性) 2
def show(e:List[Int])=println(e) // 调用自定义函数(可重用) 3
it.foreach(show)
一般集合的创建都写成常量形式
在大数据中默认使用的是不可变类型
.var
import scala.collection.immutable.Set // 导入具体类
import scala.collection.mutable._ // 导入包内所有类
import scala.collection.mutable.{ListBuffer,ArrayBuffer} // 导入包内部分类
默认导入不可变集合包
使用可变集合前需要先import scala.collection.mutable
,再通过mutable.T
使用
import scala.collection.mutable.Set
val set = mutable.Set()
scala.collection.mutable
和scala.collection.immutable
的目录结构
scala.collection.mutable
scala.collection.immutable
泛型用中括号表示:[T1,…,TN]
import scala.collection.immutable.List
import scala.collection.mutable.ListBuffer //可以代替链表高效插删
分组
list.grouped(N)
表示每N个元素为一组,若非N的整数倍,则最后一组含少于N个元素
val list = List(1, 2, 3, 4, 5, 6)
list.grouped(3).foreach(println)
//(1,2,3)
//(4,5,6)
val it: Iterator[List[Int]] = list.sliding(3,1)// 滑动窗口 list.sliding(3,3) <=> list.grouped(3)
// list.sliding(窗口长度,窗口每次移动距离)
import scala.collection.immutable.Set
import scala.collection.mutable.Set
差
差集 set1和set2的顺序会影响正负 当两个不同类型的集合进行交并差的时候 是左边的集合类型决定了结果类型
val diff: mutable.Set[Int] = set2.diff(set1)
val diff2: mutable.Set[Int] = set2 &~ set1
val union: mutable.Set[Int] = set1.union(set2) // 交集
val union2: mutable.Set[Int] = set1 | set2 // 交集
val intersect: mutable.Set[Int] = set1.intersect(set2) // 并集
val intersect2: mutable.Set[Int] = set1 & set2
import scala.collection.immutable.Map
import scala.collection.mutable.Map
val map = mutable.Map.empty;
var map = Map[String,String]("name" -> "jason","age" -> "50","test_100" -> "test_100","test_101" -> "test_101")
val map2 = Map[String,String]("brand"->"apple","sex"->"男")
map += ("city" -> "北京")
map += ("city" -> "南京")// 更新键的值
var combineMap: Map[String, String] = map ++ map2// 合并两个map
val newMap: Map[String, String] = combineMap -- map.keys// 从一个map重删去另一个map的键
combineMap -= ("city","name") // 删除指定的key
println(combineMap.get("age").get) // 获取指定key的值
println(combineMap.get("age").getOrElse("不存在"))
val map:mutable.Map[String,Int] = mutable.Map.empty
map += (("java",88)) // 外层括号表示方法的参数列表,内层括号表示是一个二元组
map ++= Array(("scala",76),("hadoop",79)) // 一次性放入
import scala.Array
import scala.collection.mutable.ArrayBuffer
val array = mutable.ArrayBuffer(
(5,100),
(3,88),
(2,60),
(4,74),
(2,52),
(5,80),
(3,83),
(1,59),
(1,77),
(4,45)
)
// 尾部追加
array.append((2,36),(4,77))
array+=((2,36))
// 向一个集合中添加另一个集合
array.appendAll(Array((1,100),(2,200)))
array++=Array((1,100),(2,200))
// 前置添加
array.prepend((2,36),(4,77))
array.prependAll(Array((1,100),(2,200)))
数组与元组
:
面向集合
val tp2s: ArrayBuffer[(Int, Int)] = array :+ Tuple2(1, 100)
val tp3s: ArrayBuffer[(Int,Int)] = Tuple2(2,100) +: array
s
插值器支持转义符
val age = 18
if(age>=18)
println(s"${age}岁是成年")
else
println(s"${age}岁是未成年")
f
插值器支持转义符和格式占位符
val height = 1.9d
val name = "James"
val introduction = f"$name%s is $height%2.2f meters tall"
raw
插值器不支持转义符
val rawString = raw"Newline character: \n"
println(rawString) // 输出:Newline character: \n,而不是换行
通配符导入
在导入包时,下划线用作通配符,表示导入该包下的所有成员。
import scala.collection.mutable._
省略参数
val nums = List(1,2,3)
val doubled = nums.map(_ * 2)
占位符语法
def add(a:Int,b:Int):Int = a+b
val addTwo = add(2,_:Int) // 创建一个新的函数,其中第一个参数固定为2
忽略参数
将不关心的参数忽略,起到一定程度上的减少冗余
val (_,value) = (1,"hello")
case _
表示没有被之前的 case
分支匹配的值,类似于 Java 中 switch-case
语句中的 default
关键字。case List(id,"java",score:Int)
用于匹配列表结构// 字符串与条件守卫
val rst = "henry@qq.com" match {
case a if a.matches("\\w+@\\w{2,}\\.(com|cn)") => "email"
case a if a.matches("1[3-9]\\d{9}") => "handset"
case "NULL" => "nothing"
case _ => "unknown"
}
// 数值
val rst = 1 match {
case 1 => "charge"
case 2 => "find balance"
case 3 => "promotion"
case _ => "not supported"
}
// 元组
val rst = (7,101) match {
case (3,_) => (3,1)
case (5,a) => if(a%2==0) (5,0) else (5,1)
case _ => (0,0)
}
// 列表
val rst = List(1,"hive",88) match {
case List(id,"java",score:Int) => if(score>=60) ("java","pass") else ("java","fail")
case _ => ("SKIP","NONE")
}
// 类型
val rst = value match {
case a:Int => ("int",a)
case a:(Int,Int) => ("Tuple2",a)
case a:String => ("String",a)
case _ => ("Unknown",1)
}
// 嵌套
val rst = (1,("java",67)) match {
case (1,(sub:String,score:Int)) => (sub,score)
case _ => ("UnknownFormat",1)
}
从Java->Scala,为什么我们需要划分方法与函数的界限?
在Java中,"方法"这个术语被用来描述类或对象的行为,而Java没有像"Scala"那样显式地使用"函数"这个概念。这是因为Java主要是一种面向对象的编程语言,而不直接支持函数式编程的特性。
Scala是多范式编程语言,支持面向对象和函数式编程两种范式。在Scala中,函数是一等公民,意味着它们可以像任何其它值一样被传递和操作。这种设计决定了方法(定义在类或对象中的行为)和函数(可以独立于类或对象存在的代码块)之间有明确的区别。
方法与函数的异同·
同:都可以执行代码块并返回结果。
异:方法不能直接作为值传递,而函数可以直接作为值传递。
方法调用依赖于对象实例或类,而函数可以独立于任何类或对象存在。
将函数作为参数传递的三种方式
def show(e:List[Int])=println(e)
it.foreach(show) // 【自定义】函数(重用) 3
it.foreach(e=>println(e)) // 【自定义】函数(一次性) 2
it.foreach(println) // 调用scala【已定义】函数 1
创建函数
val func:(参数类型) => (返回类型) = (参数列表) => {函数体}
// 接受一个整数列表作为参数并返回列表中所有元素之和的函数
val sum:(List[Int])=>Int=(numbers)=>{
numbers.sum
}
创建方法
def func(参数列表):返回类型={方法体}
// 求和方法
def add(a:Int,b:Int):Int={
a+b
}
可变参数:一个参数列表只能有一个可变参数,且必须在参数列表的末尾
def func(...,可变参数:可变参数类型*):返回类型={方法体}
def func(numbers:Int*):Int={
numbers.sum
}
函数作为参数:主流
def func(...,参数方法名:(参数列表)=>返回类型,...):返回类型={方法体}
def applyOperation(x:Int,y:Int,operation:(Int,Int)=>Int):Int = operation(x,y)
val sum = applyOperation(5,3,(a,b)=>a+b)
println(sum)
def func(init:Int,a:Int*) = {...} => def func(init:Int)(a:Int*) = {...}
def func1(init: Int)(a: Int*): Int = init + a.sum
def func2(init: Int, a: Int*): Int = init + a.sum
val result1 = func1(10)(1, 2, 3) // 需要两步调用,func1返回一个函数
val result2 = func2(10, 1, 2, 3) // 一步调用,直接传递所有参数
println(result1) // 输出:16
println(result2) // 输出:16
func1
是一个柯里化函数,是将接收多个参数的函数转换成接受单一参数的函数的过程,这些单一参数的函数返回接收下一个参数的新函数。
柯里化的作用:1.可以固定一部分参数,便于进行函数的服用
2.有利于进行延迟计算
隐式参数(implicit)
implicit val f = (a:Int,b:Int) => a/b
def divide(init:Int)(a:Int*)(implicit f:(Int,Int)=>Int):Int = f(init,a)
implicit val divider: (Int, Int) => Int = (a, b) => a / b
def divide(init: Int)(a: Int*)(implicit f: (Int, Int) => Int): Int = f(init, a.sum)
val result = divide(10)(1, 2, 3) // 使用隐式参数divider
println(result) // 输出:2
隐式参数允许你省略调用函数时传递常用或可以由上下文推断的参数,简化代码。
隐式参数中需要将函数作为方法参数
// new 关键字让所有元素初始化为0
val arr1 = new Array[Int](8)
val arr2 = Array[Int](10)
val arr3 = Array("hadoop","storm","spark")
import scala.collection.mutable.ArrayBuffer
val empty: ArrayBuffer[Int] = ArrayBuffer.empty
val ab1: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
val length = buffer.length
val bool = buffer.isEmpty
val bool = buffer.nonEmpty
val bool = buffer.forall(f: T => Boolean) // 检查是否所有元素都满足条件
println(array.forall(_ >= 10)) // 判断数组是否所有元素都>=10
val bool = buffer.exists(f: T => Boolean) // 检查是否存在满足条件的元素
println(array.exists(_ >= 10)) // 判断数组是否存在>=10的元素
val bool = buffer.contains(t: T)
val bool = buffer.containsSlice(Array(10,9,20)) // 是否包含完整的子序列(数量,顺序,内容)
array.corresponds(Array(16, 20, 14, 11, 6, 9, 8, 20))(_>_) // 判定该数组的每个元素是否与传入的数组序列的每个元素都符合传入函数的规则
array.sameElements(Array(17,21,15,12,7,10,9,20)) // 判定该数组的每个元素是否与传入的数组序列的每个元素相同
val bool = buffer.startsWith(Array(17,21,15))// 判定数组是否是以该元素片段开头
val bool = buffer.endsWith(Array(10,9,20))// 判定数组是否是以该元素片段结尾
val reverse: ArrayBuffer[T] = buffer.reverse
val index = buffer.indexOf(15)// 查找该元素在数组中对应的下标
val index = buffer.indexOfSlice(Array(7, 10, 9))// 查找该片段在数组中的第一个元素的下标
var index = array.indexWhere(_ < 10)// 查找第一个符合条件的元素下标
val lastIndex = buffer.lastIndexOf(15)// 从尾部开始查找,查找该元素在数组中对应的下标
val lastIndex = buffer.lastIndexOfSlice(Array(7, 10, 9))// 从尾部开始查找,查找该片段在数组中的第一个元素的下标
var lastIndex = array.lastIndexWhere(_ < 10)// 从尾部开始查找,查找第一个符合条件的元素下标
val ints: Array[Int] = array.clone()// 复制数组的元素和结构
array.copyToArray(arr,0,2)// 将array数组中下标范围为[0,2)的元素拷贝到arr数组中
array.copyToBuffer
// buffer.append(e:T*) +=
buffer.append((1,111))
// buffer.prepend(e:T*) +=:
buffer.prepend((2,112))
// buffer.appendAll(es:TraversableOnce) ++=
buffer.appendAll(Array((3,113)))
// buffer.prependAll(es:TraversableOnce) ++=:
buffer.prependAll(Array((4,114)))
buffer.insert(0,(3,112))
buffer.insertAll(0,Array((1,451)))
// 最终类型由左侧表达式的类型决定
val combine:ArrayBuffer[T] = buffer ++ seq:GenTraversableOnce[T]
val newBuffer: ArrayBuffer[T] = buffer :+ ((4,5)) // 用于在数组缓冲的末尾添加元素,buffer:+t即将元素添加到buffer的末尾
val newBuffer: ArrayBuffer[T] = ((4,5)) +: buffer// 用于在数组缓冲的开头添加元素,buffer:+t即将元素添加到buffer的末尾
如何理解:+
和+:
?
可以将:
看作是数组缓冲的象征,而+
指向要添加的元素的位置,buffer :+ t
可以被视为将元素t
添加到buffer
的末尾
val left: ArrayBuffer[T] = buffer - (T)
val left: ArrayBuffer[T] = buffer -- (seq:TraversableOnce[T])
buffer -= (T)
buffer --= (seq:TraversableOnce[T])
T t = buffer.remove(index:Int)// 删除指定下标的元素,并返回被删除的元素
buffer.remove(index:Int, count:Int)// 从index位置开始,删除count个元素
buffer.clear()
val left1: ArrayBuffer[T] = buffer.drop(size:Int) // 左删除
val left2: ArrayBuffer[T] = buffer.dropRight(size:Int) // 右删除
// 元素已升序排序时推荐 : 从左1开始,删除连续满足条件的元素,若左1就不满足则不删除
val left3: ArrayBuffer[T] = buffer.dropWhile(f:T=>Boolean) // 左删
buffer.trimEnd(size:Int) // 右删除size个元素
buffer.trimStart(size:Int) // 左删除size个元素
buffer.reduceToSize(size:Int) // 削减容量(左保留)
val distinct: ArrayBuffer[(Int, Int)] = buffer.distinct // 去重
array.patch(from,seq,replaced)
replaced=0=>插入 replaced>0&&seq.nonEmpty()=>替换 replaced>0&&seq.isEmpty()=>删除
Option
类型用于表示一个值可能存在也可能不存在的情况。Option
有两种形态:Some
和 None
。Some
包含一个值,而 None
表示没有值。使用 Option
可以避免空指针异常。
array.update(2,30)// 将下标为2的元素值改为30
val upd: Array[Int] = array.updated(2, 30) // 返回一个新数组
val upd = array.patch(2, Array(100, 200), 2) // 将从下标2开始的两个元素分别替换为100和200
val upd2 = array.patch(2, Array[Int](), 2) // 相当于删除
val item = buffer.apply(index:Int)// 用于获取 ArrayBuffer 中给定索引位置的元素
val item = buffer.applyOrElse(index:Int,f:Int->T)// 尝试获取给定索引的元素,如果索引超出范围或不存在,则调用函数 f
val opt: Option[(Int, Int)] = buffer.find(f:T=>Boolean)// 搜索第一个满足给定条件的元素,并返回一个 Option 类型的结果。如果找到符合条件的元素,则返回 Some(元素);如果没有找到,则返回 None。
val arr = Array(2,1,3,4,5)
println("arr.sum="+arr.sum)// 求和
println("arr.max="+arr.max)// 求最大值
println("arr.sorted="+arr.sorted.toBuffer)// 排序
array.patch(from,seq,replaced)
replaced=0=>插入 replaced>0&&seq.nonEmpty()=>替换 replaced>0&&seq.isEmpty()=>删除
Option
类型用于表示一个值可能存在也可能不存在的情况。Option
有两种形态:Some
和 None
。Some
包含一个值,而 None
表示没有值。使用 Option
可以避免空指针异常。array.update(2,30)// 将下标为2的元素值改为30
val upd: Array[Int] = array.updated(2, 30) // 返回一个新数组
val upd = array.patch(2, Array(100, 200), 2) // 将从下标2开始的两个元素分别替换为100和200
val upd2 = array.patch(2, Array[Int](), 2) // 相当于删除
val item = buffer.apply(index:Int)// 用于获取 ArrayBuffer 中给定索引位置的元素
val item = buffer.applyOrElse(index:Int,f:Int->T)// 尝试获取给定索引的元素,如果索引超出范围或不存在,则调用函数 f
val opt: Option[(Int, Int)] = buffer.find(f:T=>Boolean)// 搜索第一个满足给定条件的元素,并返回一个 Option 类型的结果。如果找到符合条件的元素,则返回 Some(元素);如果没有找到,则返回 None。
val arr = Array(2,1,3,4,5)
println("arr.sum="+arr.sum)// 求和
println("arr.max="+arr.max)// 求最大值
println("arr.sorted="+arr.sorted.toBuffer)// 排序
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。