定义一个完整的函数
val fun : (Int , Int) => Int = ( x , y ) => x + y 这是一个完整的函数定义
fun是函数的名字(Int,Int)是参数的数据类型Int是返回值类型(x,y)是参数x+y是函数体
val fun = (x: Int , y: Int) => x + y 这是简写的函数等同于上面的
注意返回值类型一定要写
函数 的下滑线_简写方法
val arr = Array(1,2,3) 定义一个数组
arr.map(x => x * 10) 这是定义在map里的函数
arr.map(_ * 10) 这是上面的简写,以后如果遇到这种格式都可以这样简写
小练习关于下划线的
val lines = Array("spark,hadoop,Flink,spark", "Spark,flink,spark,hadoop", "spark,flink,hive")
val reverse = lines.flatMap(_.split(",")).groupBy(_.toLowerCase).map(i => (i._1, i._2.length)).toList.sortBy(-_._2)
注意下划线孤零零的一个不可以用,代表两个相同的参数的时候也不可以用
小练习求每个单词的个数
val tu = List(("hsdoop", 1), ("hsdoop", 2), ("hsdoop", 3), ("have", 1), ("have", 2), ("have", 3))
tu.groupBy(_._1).map(i => (i._1 , i._2.map(_._2).sum))
小练习分开合并
val list2 = List(("hsdoop","1,2,3"),("have","1,2,3"))
val tuples1 = list2.flatMap(i => i._2.split(",").map(t => (i._1, t.toInt)))
注解:就是把元组后面的字符串切割map和单词拼成新元组
常用方法
val arr = Array(1,2,3)
arr.sum 求和
arr.product 成积
reduce 是聚合的意思
arr.reduce((x: Int, y:Int) => x + y) 这也是求和
arr.reduce((x , y) => x + y) 这是简写
arr.reduce(_ + _) 这是下滑线代替
arr.reduceLeft(_ + _) 从左开始加跟reduce一样
arr.reduceRight(_ + _) 从有开始加,但是减的时候是从又开始减就不一样了
累加的零时变量在左下一个取值在右
arr.fold(0)(_ + _) 叠加 第一个参数是初始值 注意相加初始值是0相乘初始值要是1
arr.foldLeft(0)(_ + _)
arr.foldRight(0)(_ + _) 1-(2-(3-0)) 结果是2
mapValues如果k原封不动的拿出来就可以用这个方法对v进行操作返回的还是元组k是原封不动的k, 然后v是操作过的
排序sortBy,sorted,sortWith
val arr = Array(2,1,3,6)
arr.sortBy(_ +"") 这是排序如果倒叙就.reverse
arr.sortBy(_.toString)括号里函数会改变排序规则从int排序变成String字典排序但不会改变数据本身数据类型
arr.sorted 这也是排序正序
arr.sorted(Ordering[Int].reverse) 这是倒叙但是效率比sortBy高很多
arr.sortWith((a,b) => a>b) 这是降序
arr.sortWith((a,b) => a<b) 这是升序
1.compareTo(3) 1和5作比较成立返回1不成立返回-1
take的使用
val arr = Arry(5,6,2,4,3,8,7)
arr.take(n) 从数组中取n个(从头开始取,不排序)
arr.takeRight(n) 从数组的尾部(右边)取n个
arr.filer(_ > 5) 过滤取数组中符合条件的数括号里写的是过滤条件
arr.sorted.take(n) 相当于加了个排序取最小的n个
arr.sorted.takeRight(n) 相当于加了个排序取最大的n个 (6,7,8)
arr.sortWith(_ > _ ).take(n) 这个也是取最大的n个区别在于(8,7,6)
min和max的使用和并行化集合
val arr = Array(5,6,2,4,3,8,7)
arr.aggregate(0)(_ + _, _ + _) 这个和sum的结果是一样的初始值是0,其实第二个+号就没用到
arr.min 求最大值
arr.max 求最小值 但是这两个效率不高 要两两比留大的效率才最高
arr.reduce(Math.min(_,_)) 求最小值
arr.reduce(Math.max(_,_)) 求最大值
并行化集合就是调用一个par方法比如在这个Array集合里面调用后生成一个新的Array对这个Array进行计算
val arrpar = arr.par
arrpar(_ * 10)这样计算是把他分成几块局部运算再聚合会块效率高
scala中的类的定义
scala中类直接就class加类名加大括号就好了
class people { }
如果再其他new这个people对象出来 people中用val修饰的变量不可以更改,var修饰的才可以更改
但是如果变量再加一个private这个私有的属性就都不能访问了
这时候在此文件中在创建一个同名的object类就有可以访问了这个类就叫做半生对象
半生对象: 名称一样,并且在同一文件中的object类
可以相互访问对方的private修饰的私有的属性和方法
如果这个变量用private[this]修饰那半生对象也访问不了只能定义一个get方法访问这个方法
private[this] var weigh = "asd"
private def getweigh() ={weigh}
private还可以写在类的前面后面跟中括号里面写包名
意思就是这个类只能在这个包里或者这个包的子包才能访问new的时候导包就行了
如果不在这个中括号里写的子包下new 的时候就算导包也不好使
类前面直接写private不写中括号加包名效果也一样当前包及其子包才有访问权限
如果在类名后面加private就是一个私有的构造器只能在半生对象中访问
scala的构造器
如果访问这个方法是类名点方法名就可以断定访问的是object类
因为object相当于静态的所以用类名点方法名直接访问
主构造器
scala的构造器更加的简洁, 应为scala的构造器可以和类名交织在一起,也就是共用一个名字,
就是这个Scala类的主构造器, 如果是无参的可以不写括号,也就是说空参构造可以什么都不写
因为scala的主构造器跟类交织在一起,之前Java的空参构造都是private test(){运算逻辑},这时候scala
的运算逻辑就可以直接写在类名后面的大括号里
如果在类后面的大括号里直接在大括号那么new这个类的时候也会被执行,这个大括号就相当于构造代码块
辅助构造器
在构造器传递的参数可以用val,var修饰,修饰的参数会成为类的成员变量
如果不用var,val修饰只是形参,java中就只是形参,可以在类中在定义同名的成员变量,但是想访问还是类里的属性
定义辅助构造器,辅助构造器一定要用def this,辅助构造器一定要调用主构造器或者其他构造器,但是其他
构造器还要调用主构造器
定义构造器的时候一定要把少的参数定义为主构造器
辅助构造器里的参数不用加var,val修饰
注意辅助构造器第一行一定要调主构造器this(name,age)
注意辅助构造器新加的参数要在成员变量中添加var修饰
//这个是类也是主构造器
class scala05 (val name :String ,var age :Int){
//这是两个成员变量是辅助构造器新加参数必须定义的给一个默认值
var shengao:Int =_
var xingbie:String=""
//这个是辅助构造器
def this(name : String ,age :Int , shengao:Int){
//这边调用了主构造器
this(name,age)
this.shengao=shengao
}
//这又是一个辅助构造器
def this(name : String ,age :Int , shengao:Int,xingbie:String){
//这个调用的是上一个辅助构造器
this(name,age,shengao)
this.xingbie=xingbie
}
}
object scala05{
def main(args: Array[String]): Unit = {
//这边写了一个类new上面的辅助构造器
val en = new scala05("dezhi", 18,170,"nan")
println(en.xingbie)
}
}
单例对象
在scala中没有静态方法和字段,但是可以用object这个语法结构达到同样的目的
1.存放工具方法和常量2.高效共享单个不可变的实例3.单例模式
synchronized锁
//这是定义的成员变量
var m=10
//这便用synchronized锁把方法锁起来了一次只能进来一个运算逻辑就不会共享冲突
def yi(i :Int) =synchronized{
m = m+i
m
}
def er(i :Int) ={
println(123)
//因为上面有和共享变量无关的运算逻辑可以只锁有共享的逻辑这样更加高效
synchronized{
m = m+i
m
}
}
}
scala中object类就不能new可以直接val a=test这个object类名就可以用里面的成员变量
当打印这个a的时候里面的静态的东西都会被加载,单之后无论实例化几次,这个静态的东西都只是加载一次
apply方法
下面写的几个参数的apply方法其实就是方法重载
apply方法不仅仅再object中能定义,再class中也能定义并且也是可以直接类名调用不用点apply方法名
object applydemo {
def apply()={ //创建一个空参的apply方法
println("no")
}
def apply(a:Int,b:Int): Unit ={
println("two Int")//创建两个Int为参数的apply方法
}
def apply(a:Int,b:Double): Unit ={
println("one Int one Double")//创建一Int一Double的apply方法
}
}
object test{
def main(args: Array[String]): Unit = {
applydemo.apply(1,1)//这是方法重载
applydemo(1,3.3) //第二种调用apply方法的方法,不用点apply方法名
}
}
继承和实现特质
abstract 抽象类
trait 特指(就是接口)
extends 实现抽象类
with 实现特指
注意重写未实现的方法可以加override也可以不加如果重写已经实现的方法一定要加override关键字,无论是继承抽象类还是实现特指第一个一定要写extends,未实现的方法一定要实现Ctrl+i,已经实现的方法也可以重写Ctrl+o
---------------------------------定义了一个抽象类--------------------------------------
abstract class dongwu {
def run():Unit//未实现的方法
def breath (): Unit ={
println("呼吸")
}
}
---------------------------------定义了一个特指---------------------------------------
trait dajia {
def daren() :Unit//未实现的方法
}
--------------------------------定义了一个特指-----------------------------------------
trait fly {
def fei():Unit//未实现的方法
}
---------------------------------------------------------------------------------------
创建一个类exdenes继承抽象类with实现特指再with实现特指
class monkey extends dongwu with fly with dajia {
//实现接口重写的daren方法
override def daren(): Unit = {
println("棒打鸳鸯")
}
//重写接口fly的fei的方法
override def fei(): Unit = {
println("起飞")
}//重写抽象(未实现的方法)可以加override,也可以不加
override def run(): Unit = {
println("快跑")
}//重写已经实现的方法必须加上override
override def breath(): Unit = {
println("呼吸新鲜空气")
}
}
---------------------------------------------------------------------------------------
动态混入特指的用法
class dog {
}//创建一个类new这个类的时候就可以动态混入特指
object dog{
def main(args: Array[String]): Unit = {
//动态混入特指,直接with加特指名再加一个也是这样重写方法直接加大括号重写
//注意只能with特指也就是trait
val d = new dog with fly with dajia {
override def daren(): Unit = {
println("咬人")
}
override def fei(): Unit = {
println("起飞")
}
}
d.fei()
}
}
case class类详解
@BeanProperty 添加get,set方法
@beanGetter 添加get方法
@beanGetter 添加set方法
override def toString: String = s"name: $name, age: $age" 这是一种重写tostring方法的格式
//也是一个特殊的class,可以创建多个实例,每个实例封装自己的数据
//可以用于模式匹配
//可以new 也可不用new,内部实现了apply方法
//默认实现序列化接口意思就是默认实现Serializable(实现序列化接口)
//默认重写了toString方法
//case中主构造器中的变量不加val、var默认数据val的,如果想重新赋值,要加上var
@BeanProperty
case class CStudent(
name: String,
var age: Int
) {
//自己手写的getter setter方法
def getAge(): Int = {
this.age
}
def setAge(age: Int): Unit = {
this.age = age
}
//重写tostring方法的一种简便格式
override def toString: String = s"name: $name, age: $age"
}
//定义一个object类测试
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val cs1 = new CStudent("jerry", 10)
println(cs1)
println(cs1.age)
println(cs1.name)
cs1.age = 12
println(cs1)
val cs2 = CStudent.apply("tom", 10)
println(cs2.hashCode())
println(cs2.age)
println(cs2.name)
}
}
如果之这个user类实现了Serializable(序列化接口)就可以写到这个路经下
val oos = new ObjectOutputStream(new FileOutputStream("/Users/xing/Desktop/out/file1"))
oos.writeObject(new user)
oos.flush()
oos.close()
可是如果是一个case class类就默认实现序列化接口就可以直接写到相应路径下
val oos = new ObjectOutputStream(new FileOutputStream("/Users/xing/Desktop/out/file1"))
oos.writeObject(cs2)
oos.flush()
oos.close()
模式匹配
定义了一个数组取索引赋值给name跟cas后面的字符串作比较相同就执行其他都不执行,搞个_通配如果没有
相同的就走通配符后面的代码,就类似于else if
object test03 {
def main(args: Array[String]): Unit = {
val arr = Array("YoshizawaAkiho", "YuiHatano", "AoiSola")
val name = arr(2)
println(name)
name match {
case "YoshizawaAkiho" => {
println("吉泽老师...")
}
case "YuiHatano" => println("波多老师...")
case _ => println("真不知道你们在说什么...")
}
}
}
这个和上面类似但是给一个变量名冒号数据类型就可以匹配数据类型
object test04 extends App {
//val v = if(x >= 5) 1 else if(x < 2) 2.0 else "hello"
val arr: Array[Any] = Array("1", 1, 2.0)
val v = arr(0)
//val v: Any = 2.0
println(v)
v match {
case s: Int => {println("Int " + s)}
case y: Double if(y >= 3) => println("Double "+ y)
case z: String => println("String " + z)
case _ => throw new Exception("not match exception")
}
}
上面几张图片是list集合的几个方法
list.head 取第一个
list.tails 返回个迭代器
list.tail 去第一个
+: 再前面加
:+ 再后面加
Nil 空list
++ 两个list合并
:: 前面变成后面的第一个元素
以上都是生成一个新的list源list还在
object CaseDemo03 extends App{
//匹配数组
// val arr = Array(1, 3, 5)
// arr match {
// case Array(1, x, _) => println(x + " ")
// case Array(0) => println("only 0")
// case Array(0, _*) => println("0 ...")
// case _ => println("something else")
// }
//匹配集合
// val lst = List(0, -1, 3)
// lst match {
// case 0 +: Nil => println("only 0")
// case x :: y :: Nil => println(s"x: $x y: $y")
// case 0 :: tail => println("0 ...")
// case _ => println("something else")
// }
//匹配元组
val tup = (2, 3, 5)
tup match {
case (2, x, y) => println(s"2, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
这个是偏函数的一个举例
object PartialFuncDemo {
//PartialFunction类型的方法就叫作偏函数
def func1 : PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("two"))
println(func2("two"))
}
}
这个也是偏函数
val lst = List((1, 2.0, 3L, "abc"), (1, 2.0, 3L, "def"))
lst.map {
case (id, money, _, name) => {
(id + name, money)
}
这个是模式匹配匹配类的一个举例
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long) extends Product
//样例对象,单例的,在一个JVM进程中,只有一个实例
case object CheckTimeOutTask
object CaseDemo04 extends App {
val arr: Array[Product] = Array(CheckTimeOutTask, new HeartBeat(12333) , HeartBeat(5555), SubmitTask("0001", "task-0001"))
val r = arr(Random.nextInt(arr.length))
println(r)
r match {
case SubmitTask(id, name) => {
println(s"$id, $name")
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
模式匹配的一个小案例主要讲了一个getOrElse的方法好用不会强制报错
object OptionDemo {
def main(args: Array[String]): Unit = {
val mp: Map[String, Int] = Map("a" -> 1, "b" -> 2)
//val r: Int = mp.apply("c")
//println(r)
// val op: Option[Int] = mp.get("b")
// //println(op)
// val r = op match {
// case Some(x) => x
// case None => 0
// }
val r = mp.getOrElse("b", -1)
println(r)
}
}
函数的深入理解
片名的理解函数可以是一个方法应为底层调用了apply方法
下面是多种方式定义了同一个函数
bject FuncInDeep {
//函数本质就是一个引用类型,函数就可以作为参数传入到方法中,也可以作为方法的返回值
val func = (x: Int, y: Double) => x + y
val func2: (Int, Double) => Double = (x, y) => x + y
val func4: Function2[Int, Double, Double] = (x, y) => x + y
//真正函数的定义方式
val func3: Function2[Int, Double, Double] = new Function2[Int, Double, Double] {
override def apply(v1: Int, v2: Double): Double = v1 + v2
}
def main(args: Array[String]): Unit = {
val res: Double = func3.apply(3, 4.0)
val tp: (Double, Int) = (1.0, 3)
val tp2: (Double, Int) = new Tuple2[Double, Int](1.0, 3)
val tp3: Tuple2[Double, Int] = new Tuple2[Double, Int](1.0, 3)
val tp4: Tuple2[Double, Int] = (1.0, 3)
}
}