Scala中每个值都是一个对象
Scala严格区分方法和函数
轻量级语法定义匿名函数,支持高阶函数,允许函数嵌套
动态语言:
不用事先声明;
随时可以为其赋值(任意类型);
编译时不知道是什么类型
强类型:不同类型之间的操作,必须强制类型为同一种数据类型
弱类型:不同类型之间可以直接操作,存在隐式转换
https://www.scala-lang.org/
idea下载Scala插件
https://docs.scala-lang.org/zh-cn/tour/tour-of-scala.html
大体同Java
object关键字定义的类的main方法为入口方法
分号不是必须的,一般也不写
return也不是必须的,一般也不写
Scala中所有地方,默认最后一条语句的结果作为返回值
object Demo {
def main(args: Array[String]): Unit = {
println(1, 3, 4, 5)
print("结束"+0)
}
}
var|val 变量名 [:数据类型] = 变量值
数据类型可以自动推断(赋值的情况),根据值的类型推断
可以同时给多个变量赋值:
val (a,b,c) = (1,'p',"c") //类型自动推断
// a = 1
// b = 'p'
// c = "c"
var d,e = 10
// d = 10
// e = 10
使用var定义变量代表改变量的值可变
使用var定义变量代表改变量的值不可变
Scala中所有数据类型皆为引用数据类型
使用val定义的变量的值(地址)不可变,但其引用的对象中的值(对象的属性)可以改变
相当于Java中使用了final关键字
关键字,惰性变量,只有val修饰的变量可以使用lazy修饰。
使用lazy定义的变量,只有在调用的时候才会实例化这个变量。
scala> lazy val a = 100
a: Int =
scala> a
res0: Int = 100
https://docs.scala-lang.org/resources/images/tour/unified-types-diagram.svg
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XkTI6HP7-1570532480980)(E:\大数据工具下载\scala\笔记\assets\1569665608894.png)]
Unit类似于Java中的void,但是Unit可以作为返回值进行接收
Nothing是所有类型的子类,但是它不存在,它没有具体的实例对象,一般常用引用、抛异常、程序exit退出、无限循环
Null 是代表没有引用多用于 ,集合,数组,Option,自定义类的赋值
相当于Java中的八个基本数据类型的包装类 和 一个Unit
这八个基本数据类型不是Java中的包装类,比Java中的包装类强大一些
整数类型默认推断为Int
浮点类型默认推断为Double
字符类型可以写Unicode值,如'\u0093'
完全是Java中的String类
Any相当于Java中的Object类,是所有类的根类
Anyval 接收基础数据类型
AnyRef 接收引用类型
Scala操作符和Java使用方式一致
没有++
没有–
可以使用其他的代替
如+=
如-=
Scala重载了一大堆运算符
if(条件表达式){
执行代码
}
val x = 3
if(x > 2){
println("x>2")
}
val x2 = if(x > 2){
x - 2
}else if(x < 0){
"x的值小于0"
}else {
x
}
pirntln(x2)
三种循环:while、do-while、for
while与do-while与Java一模一样,不写了
for循环与Java完全不同
for(变量 <- 范围 [守卫]){
执行语句
}
这个范围可以是表达式、数组、集合
这个守卫是一个if语句,如if(i>0)
i为变量
括号可以省略
for (i <- 1 to 10 if i % 2 == 0){
println(i)
}
这里的to是一个方法,同1.to(10)
,代表从1到10,包含1和10
还有一个方法until,1.until(10)
,同样代表1到10,包含1,但是不包含10
1 to 10
1 until 10
1.to(10)
1.until(10)
两种写法是一样的,Scala支持这么写
Scala里面推荐使用函数式的风格解决break和continue的功能,而不是一个关键字
import scala.util.control.Breaks._
// break
breakable{
for(i <- 1 to 10){
if(i == 8) break
}
}
// continue
for(i <- 1 to 10){
breakable{
if(i == 8) break
}
}
练习,万年历
import scala.io.StdIn
/**
* @author ZJHZH
*/
object Exercise {
def main(args: Array[String]): Unit = {
//输入年月
print("请输入年份:")
val year: Int = StdIn.readInt()
print("请输入月份:")
val month: Int = StdIn.readInt()
// 是否闰年
val flag = leap_year(year)
// 月天数
val days = getDays(month, flag)
// 距离1900年1月1日的总天数
val day1 = getDaysfrom1900(year)
// 距离当年(输入年)一月一日的天数
val day2 = getDayYear(month)
// 获取第一天周几
val day_week = getDayOfWeek(day1 + day2)
// 打印日历
showCalendar(days, day_week)
}
/**
* 判定当前年是不是闰年
*
* @author ZJHZH
* @param year 年份
* @return Boolean
*/
def leap_year(year: Int): Boolean = {
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
return true
} else {
return false
}
}
// /**
// * 获取每月最大天数
// *
// * @author ZJHZH
// * @param year
// * @param month
// * @return Int 天数
// */
// def getMaxDaysMonth(year: Int, month: Int): Int = {
// val date1 = Calendar.getInstance()
// date1.set(Calendar.YEAR, year)
// date1.set(Calendar.MONTH, month)
// date1.getActualMaximum(Calendar.DATE)
// }
/**
* 获取当月最大天数
*
* @param month
* @param flag 是否闰年
* @return Int 天数
*/
def getDays(month: Int, flag: Boolean = false): Int = {
if (month == 2) {
if (flag) {
return 29
} else {
return 28
}
}
if (month == 4 || month == 6 || month == 9 || month == 11) {
return 30
}
return 31
}
/**
* 循环
* 使用循环计算1900年到输入年份之间的天数,不包含输入年份
*
* @param year
* @return
*/
def getDaysfrom1900(year: Int): Int = {
var days = 0
for (i <- 1900 until year) {
if (leap_year(i)) {
days += 366
} else {
days += 365
}
}
return days
}
/**
* 使用循环计算用户输入的月份距离当年1月1日共多少天
*
* @param month
* @param flag 是否闰年
* @return Int 天数
*/
def getDayYear(month: Int, flag: Boolean = false): Int = {
var days = 0
for (i <- 1 until month) {
days += getDays(i, flag)
}
return days
}
/**
* 获取当月第一天是周几
* 0 -> 周日
* 6 -> 周六
*
* @param days
* @return
*/
def getDayOfWeek(days: Int): Int = {
(days + 1) % 7
}
def showCalendar(days: Int, day_Week: Int): Unit = {
println("日\t一\t二\t三\t四\t五\t六\t")
var week = 0
for (i <- 0 until day_Week) {
week += 1
print("\t")
}
for (i <- 1 to days) {
if (week >= 7) {
println()
week = 0
}
print(i + "\t")
week += 1
}
}
}
定义方法使用def关键字
def 方法名(参数列表)[:返回值类型] = {
方法体
}
// 标准版
def getDayOfWeek(days: Int): Int = {
return (days + 1) % 7
}
// 简化版
def getDayOfWeek(days: Int) = {
(days + 1) % 7
}
// 再简化,因为方法体只有一条语句,所以:
def getDayOfWeek(days: Int) = (days + 1) % 7
// 也可以这么写,等号大括号二选一保留
def getDayOfWeek(days: Int){(days + 1) % 7}
// 没参数,也没有返回值
def method1() = println("打印方法")
// 更简一步
def method2 = println("更简")
// 默认值
def sum(x:Int,y:Int = 2) = x + y
// 这时候在调用的时候就 可以 只传x的值,同样可以执行
// 可变参数
def sum(x:Int*) = {
var num = 0
for (i <- x){
num += i
}
num
}
// 调用可变参数
sum(1,2,3,4)
// 传入数组时,需要转换类型才能传入,转变方式在数组或数组名后面添加:_*
// 注意这个下划线
sum(Array(1,2,3,4):_*)
// 递归,方法支持递归,但是一定要写明返回值类型
def fab(n:Int):Int={
if((n==1)||(n==2)){
return 1
}else{
return fab(n-1)+fab(n-2)
}
}
Scala中是区分函数和方法的,函数是函数,方法是方法。定义函数和定义方法的方式完全不同
但是,方法可以当函数使用,因为方法可以隐式转换为函数
函数一般写在方法体中,同样也可以写在类体中
写在方法体中的最多
函数同样可以使用return关键字,但是要注意return会结束方法,注意函数定义、执行的位置
val 函数名[:(参数类型列表)=>返回值类型]= (参数列表) => {函数体}
一般不写这个函数的类型
// 完全体
val getDays: (Int,Boolean) => Int = (month: Int, flag: Boolean) => {
var days = 0
if (month == 2) {
if (flag) {
days = 29
} else {
days = 28
}
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
days = 30
} else {
days = 31
}
days
}
// 稍简
val getDays: (Int,Boolean) = (month: Int, flag: Boolean) => {
var days = 0
if (month == 2) {
if (flag) {
days = 29
} else {
days = 28
}
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
days = 30
} else {
days = 31
}
days
}
// 最常用
val getDays = (month: Int, flag: Boolean) => {
var days = 0
if (month == 2) {
if (flag) {
days = 29
} else {
days = 28
}
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
days = 30
} else {
days = 31
}
days
}
// 整体简化规则同方法
// 但是这个不是函数,这个是变量
val function_1 = println("更简")
// 将函数转换为方法,注意这个下划线
// 格式:方法名+空格+下划线
val f2 = methodName _
// Scala是提供隐式转化的,会在传入的时候自动转换
一种存储各种对象和数据的容器
Scala中集合分为两大类:
可变集合 包:scala.collection.mutable
不可变集合 包:scala.collection.immutable
不可变集合是可以并发访问的
可不可变指的是集合的长度(大小)
Scala中默认使用的是不可变集合
Scala三大集合:
集合默认继承于Iterable
使用最多的还是List,Set,Map
Map默认是不可变的,底层实现是HashMap,可以导包,有可变的
HashMap是可变的
TreeMap是不可变的,会根据key值进行排序,key需要具备可比性,最低要求是数据类型一致
import scala.collection.mutable
object MapDemo{
def main(args: Array[String]): Unit = {
// 不可变Map
// 第一种赋值方式 Map(key->value)
val map1 = Map("zhangsan"->90,"lisi"->20,"wang"->1)
// 第二种赋值方式 元组赋值 Map((key,value))
val map2 = Map(("zhangsan",20),("lisi",19))
// 可变Map
val map3 = mutable.Map("zhangsan"->90,"lisi"->20,"wang"->1)
// 访问值,可变不可变是一样的
// 1.直接通过key访问,不推荐,不存在,抛异常
val a1 = map3("wangwu")
// 2.在1的基础上加个判断,和Java中一致,在Scala中一般般
val a2 = if(map3.contains("wangwu")) map3("wangwu") else 0
// 3.使用Scala中的get方法,返回一个Option类型的值
// Option有两个子类,some()和None
// some(value值) key存在,值取出来了,就在some中
// None key不存在,没有value值
val a3 = map3.get("lisi")
val a4 = map3.get("wangwu")
// 问题来了,some中的值怎么取出来:模式匹配
// 4.使用getOrElse(key,默认值),是第二种的综合
val a5 = map3.getOrElse("wangwu",90)
// 修改map中的信息
// 只能是可变Map
// 只能是可变Map
// 只能是可变Map
// 1.通过key修改对应的value,key不存在则添加
map3("wangwu") = 40
// 2.添加,重载了运算符
map3 += ("zhaoliu"->50)
// 3.添加另一个Map集合,添加的这个(等号右面)可变不可变无所谓
map3 ++= Map(("tianqi",20))
// 删除一个键值对,同样重载了运算符
map3 - "wangwu"
// 遍历
// keyset
for(e <- map3.keyset){
println(e)
}
// keys方法,获取迭代器
for(e <- map3.keys){
println(e)
}
// values方法,获取迭代器
for(e <- map3.values){
println(e)
}
// 直接打印Map,利用对偶元组
for((k,v) <- map3){
println(k + "\t" + v)
}
}
}
内容可变,长度不可变
// 定义方式,两种
// new
var arr1 = new Array[Int](9) // 长度为9
// 使用apply方法
var arr2 = Array(10,20,30,'b') // 常用
// 定长数组不可以直接打印,会打出数组的地址
// 可以转化数据类型 toBuffer toList ...
// 一般toBuffer,toBuffer表示转化为变长数组
println(arr2.toBuffer)
println(arr2(0)) // 打印下标0的元素
arr2(2) = 2000
// 添加元素,无法在原数组进行添加,只能拼接成一个新的数组
val ints: Array[Int] = arr2.+:(9999)
内容可变,长度也可变
// 导包
import scala.collection.mutable
// 创建变长数组,默认还是创建不可变数组,需要加上包名
var arr1 = mutable.ArrayBuffer[Int]()
var arr2 = mutable.ArrayBuffer[Int](10,20,30)
// 添加数据,不生成新数组;如果是定长数组,这时候地址会变成新生成的定长数组的地址
arr2 += 1
// 定长数组没这个方法
arr2.append(1)
// 添加多个
arr2 += (2,3,9)
arr2.append(2,3,9)
// 添加数组
arr2 ++= Array(4,5,6)
// 在指定位置插入值
// insert(下标,可变参数)
arr2.insert(1,19,18)
// 变长数组变成定长数组
var array:Array[Int] = arr2.toArray
// 定长,变长,都一样,下标形式
for (i <- 0 until arr2.length) = println(arr2(i))
// 元素遍历
for (i <- arr2) = println(arr2(i))
// 求和
arr2.sum
// 过滤,需要一个Boolean类型返回值的一个参数的函数,注意这个下划线
// (e)=>{e < 10} 简化为 e => e < 10 再简化 _ < 10
arr2.filter(_ < 10)
// 数据操作,简化方式同上
arr2.map(_ * 2)
// 排序,默认升序,你也自定义不了
arr2.sorted
// 但是可以翻转一下
arr2.sorted.reverse
数组操作生成新的数组,原数组不变,该接收的接收
一个轻量级的集合,可以存储任意数据类型,可以存多个
(e1,e2,e3,…)
获取元素:
_1:第一个
_2:第二个
……
对偶元组:以k,v形式存储的元组(俩元素的元组),最常用的元组
// 创建元组
val t1 = ("sparkcore","sparkSQL","sparkStreaming")
// 获取值,通过对应的变量来存储对应元素的值,同变量的定义:多个变量赋值
val t2,(a,b,c) = ("sparkcore","sparkSQL","sparkStreaming")
// new一个,1代表可以存无限个,3代表存三个
val t3 = new Tuple1(1,2,3)
val t4 = new Tuple3(1,2,3)
// 遍历 productElement(下标值)
for(e <- t1.productElement){
println(e)
}
// 遍历:打印:foreach
t1.productIterator.foreach(println(_))
将两个数组,集合,组成元组
object ZipDemo {
def main(args: Array[String]): Unit = {
// zip将两个集合中元素拼接成kv形式对偶元组形式
// 数组和集合用法一致
val names = List("xiaobao","xiaohong","xiaoming")
val moreList = List(List(1,2,3),List(2,3,4))
println(moreList)
val scores1 = List(16,5,9,10,20)
val tuples: List[(String, Int)] = names.zip(scores1)
val tuples1: List[(io.Serializable, Any)] = names.zipAll(scores1,"null",0)
println(tuples)
println(tuples1)
val index: List[(String, Int)] = names.zipWithIndex
println(index)
val unzip: (List[io.Serializable], List[Any]) = tuples1.unzip
println(unzip)
}
}
List可以存储不同的数据类型,并且有序,不可变
object ListDemo {
def main(args: Array[String]): Unit = {
// 创建不可变List
val empty = List()
// 创建一个有值的集合
val names = List("xiaobai","xiaohong","xiaohuang")
// 集合是可以存储多个值的(集合中存集合)
val moreList = List(List(1,2,3),List(2,3,4))
// 列表中有一个默认的空值 nil 它可以和 :: 运算符进行列表赋值操作
// Nil 和 :: 的用法
val strings:List[String] = "小明" :: "小红" :: "小黄" :: Nil
}
}
练习,一个Scala版的WordCount
package WordCount
/**
* @author ZJHZH
*/
object Scala_WordCount {
def main(args: Array[String]): Unit = {
val list: List[String] = List("hello tom hello jerry","hello suke hello"," hello tom")
val strings: List[String] = list.flatMap((_: String).trim.split(" "))
val tuples: List[(String, Int)] = strings.map(((_: String), 1))
val tuples1: List[(String, Int)] = tuples.sortBy((_: (String, Int))._1)
val stringToTuples: Map[String, List[(String, Int)]] = tuples1.groupBy((_: (String, Int))._1)
val stringToInt: Map[String, Int] = stringToTuples.map((e: (String, List[(String, Int)]))=>(e._1,e._2.map((_: (String, Int))._2).sum))
println(stringToInt)
}
}
val tuples: List[(String, Int)] = List(("hello tom hello jerry", 3), ("hello suke hello", 9), (" hello tom", 2))
val stringToInt: Map[String, Int] = tuples.flatMap((e: (String, Int)) => {
e._1.trim.split(" ").map(((_: String), e._2))
}).groupBy((_: (String, Int))._1).map((e: (String, List[(String, Int)])) => {
(e._1, e._2.map((_: (String, Int))._2).sum)
})
println(stringToInt)
val line: List[Array[String]] = List(Array(" hello tom hello jerry"), Array(" hello xiaobai hello "), Array("hello tom "))
val tuples: List[(String, Int)] = line.flatMap((_: Array[String]).flatMap((_: String).trim.split(" ")).map(((_: String), 1)))
println(tuples)
高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。
map遍历集合中的元素并可以对其进行操作。
遍历并对每一个元素进行操作
map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That
map的使用很简单:
// 一些极简单的使用
// 将list中的每个元素乘2,注意这会生成一个新的集合,需要接收
List(1,2,3,4).map(e:Int=>e * 2)
List(1,2,3,4).map(_ * 2)
// 这个函数可以写复杂,可以在外部定义好,传入即可
foreach同样是遍历集合,但是它一般不用来操作数据,一般只用来打印,和Java中使用基本一致
def sum[B >: A](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus)
求和,调用的时候一般不传参数,隐式函数参数一般情况下都不进行修改
List(1,2,3,4).sum
max,min同
def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)
reduce 需要传入一个函数,这个函数定义了计算方式
reduce方法求和的时候,这个方法需要两个参数,这两个参数有处理逻辑
第一个参数 分为
第一次 会获取集合中第一个元素的值
以后每次计算 每次获取的是上一次 第一个参数+第二参数的和
第二参数 分为
第一次 获取集合中第二个元素的值
以后每次计算 开始获取集合中第二个元素后面每一个元素的值,然后参与运算
// 单线程
val sumed3 = arr.reduce((x,y)=>{x+y})
// 多线程
val sumed3_1 = arr.par.reduce((x,y)=>{x+y})
// 求差值,在并行化中,需要使用reduceleft
refuceLeft,reduceRight使用方式同reduce,但是reduceLeft在并行化状态也是准确的,原理为:
reduceLeft在并行化中,计算依旧是从左向右计算,只有左边的计算完毕再将值带入后续计算
val i: Int = List(1, 2, 7, 4, 9, 3, 2).reduce((e1, e2) => {
println("e1\t" + e1)
println("e2\t" + e2)
e1 + e2
})
println(i)
同reduce,但是fold有两个参数列表,第一个参数列表为每次计算的默认值,第二个参数列表同reduce。
第二个参数列表为一个函数,这个函数有两个参数,同reduce,但是参数逻辑有一点点不同
第一个参数为上一次第一个参数与第二个参数计算的值(或初次计算时获取第一个参数列表的值–默认值)
第二个参数为集合中下一个没有计算的元素
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
println(List(1, 2).fold(0)(_ + _ * 2))
foldLeft,foldRight使用方式同fold,但是foldLeft是做差值也准确的,计算原理同reduce,reduceLeft
foldRight是从右向左计算的,其他同foldLeft
def forall(p: A => Boolean): Boolean = {
var these = this
while (!these.isEmpty) {
if (!p(these.head)) return false
these = these.tail
}
true
}
遍历整个集合,一旦碰到false则停止遍历,结果即为false;否则为true
println(List(1, 23, 5, 8).forall(_ < 10))
过滤器,同Java中使用一模一样。满足条件的留下,生成一个新的集合。
def filter(p: A => Boolean): Repr = filterImpl(p, isFlipped = false)
private def filterImpl(p: A => Boolean, isFlipped: Boolean): Repr = {
val b = newBuilder
for (x <- this)
if (p(x) != isFlipped) b += x
b.result
}
println(List(1, 2, 34, 6, 7, 9, 1).filter(_ > 8))
filter有一个参数列表,参数是一个函数。
filterNot逻辑与之正好相反。
排序,不需要传参,因为它有自动传入的隐式函数,一般不做处理,同样,它也会生成一个新的集合。
def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
val len = this.length
val b = newBuilder
if (len == 1) b ++= this
else if (len > 1) {
b.sizeHint(len)
val arr = new Array[AnyRef](len) // Previously used ArraySeq for more compact but slower code
var i = 0
for (x <- this) {
arr(i) = x.asInstanceOf[AnyRef]
i += 1
}
java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]])
i = 0
while (i < arr.length) {
b += arr(i).asInstanceOf[A]
i += 1
}
}
b.result()
}
println(List(1, 2, 34, 6, 7, 9, 1).sorted)
需要传入一个参数列表,是一个函数,函数有两个参数,需要一个布尔类型的返回值。
自定义排序规则
def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)
println(List(1, 2, 34, 6, 7, 9, 1).sortWith(_ > _)) // 降序
指定按什么排序。两个参数列表,第二个是隐式函数不用管。第一个参数列表是一个函数,需要一个参数,会有一个任意返回值类型,可以自定义排序规则。
def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)
println(List(1, 2, 34, 6, 7, 9, 1).sortBy(_))
分区,同SQL。返回值是一个Map集合,键值对,一一对应。参数是一个函数,函数一个参数,任意返回值类型,这函数返回值就是key,与这个key对应的就是获得这个key的参数的List集合。
def groupBy[K](f: A => K): immutable.Map[K, Repr] = {
val m = mutable.Map.empty[K, Builder[A, Repr]]
for (elem <- this) {
val key = f(elem)
val bldr = m.getOrElseUpdate(key, newBuilder)
bldr += elem
}
val b = immutable.Map.newBuilder[K, Repr]
for ((k, v) <- m)
b += ((k, v.result))
b.result
}
println(List(1, 2, 34, 6, 7, 9, 1).groupBy(e=>e))
// Map(1 -> List(1, 1), 6 -> List(6), 9 -> List(9), 2 -> List(2), 34 -> List(34), 7 -> List(7))
分区,同filter的使用,一个参数,是函数,这个函数一个参数和一个布尔类型的返回值。
def partition(p: A => Boolean): (Repr, Repr) = {
val l, r = newBuilder
for (x <- this) (if (p(x)) l else r) += x
(l.result, r.result)
}
println(List(1, 2, 34, 6, 7, 9, 1).partition(e=>e > 8))
// (List(34, 9),List(1, 2, 6, 7, 1))
查找,在集合中查找第一个满足条件的元素,条件同filter,返回值是一个Option类型。
def find(p: A => Boolean): Option[A] = {
var these = this
while (!these.isEmpty) {
if (p(these.head)) return Some(these.head)
these = these.tail
}
None
}
println(List(1, 2, 34, 6, 7, 9, 1).find(e=>e > 8))
匹配元素,直到匹配不上为止,将匹配上的返回,返回一个新的集合。
@inline final override def takeWhile(p: A => Boolean): List[A] = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
b.toList
}
println(List(1, 2, 34, 6, 7, 9, 1).takeWhile(e=>e < 8))
// List(1, 2)
获取前N个元素,从左向右。如需从右向左,可以使用takeRight。
override def take(n: Int): List[A] = if (isEmpty || n <= 0) Nil else {
val h = new ::(head, Nil)
var t = h
var rest = tail
var i = 1
while ({if (rest.isEmpty) return this; i < n}) {
i += 1
val nx = new ::(rest.head, Nil)
t.tl = nx
t = nx
rest = rest.tail
}
h
}
println(List(1, 2, 34, 6, 7, 9, 1).take(4))
// List(1, 2, 34, 6)
将复杂集合中数据进行扁平化处理,参数是一个隐式函数,不需要传参,直接用即可。
def flatten[B](implicit asTraversable: A => /*<:
是flatten和map的综合版。
将数据进行扁平处理并遍历。
final override def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = {
if (bf eq List.ReusableCBF) {
if (this eq Nil) Nil.asInstanceOf[That] else {
var rest = this
var found = false
var h: ::[B] = null
var t: ::[B] = null
while (rest ne Nil) {
f(rest.head).seq.foreach{ b =>
if (!found) {
h = new ::(b, Nil)
t = h
found = true
}
else {
val nx = new ::(b, Nil)
t.tl = nx
t = nx
}
}
rest = rest.tail
}
(if (!found) Nil else h).asInstanceOf[That]
}
}
else super.flatMap(f)
}
println(List(List(1, 2, 3), List(4, 5, 6), List(7, 8), List(9)).flatMap(_.toList))
// List(1, 2, 3, 4, 5, 6, 7, 8, 9)
def aggregate[S](z: =>S)(seqop: (S, T) => S, combop: (S, S) => S): S = {
tasksupport.executeAndWaitResult(new Aggregate(() => z, seqop, combop, splitter))
}
两个参数列表,第一个参数列表一个参数,为默认值,又或者叫初值;
第二个参数列表两个参数,为两个函数,第一个参数为分片内的计算规则,第二个参数为分片之间的计算规则。
单说分片内的计算逻辑与分片之间的计算逻辑都与fold,reduce没什么区别。这个默认值会在没个分片之内计算的时候调用一次。
println(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).par.reduce(_ - _))
// 5,这个差值明显不正确
println(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).par.reduceLeft(_ - _))
// -53
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).par.reduce((e1,e2)=>{
println("e1\t" + e1)
println("e2\t" + e2)
e1 - e2
})
面向对象思想和Java中是一样的,只不过语法不同。
Java | Scala | |
---|---|---|
class | class和object | |
访问权限修饰符 | public、protected、default、private | public、private |
构造方法 | 类内部 | 默认构造和辅助构造 |
抽象类 | 抽象方法必须使用abstract关键字 | 允许属性重写 |
接口 | 从JDK1.8开始允许写实现方法(default或static修饰) | 名为特质,允许写实现方法 |
class类本身 | 有伴生类,伴生对象 |
在scala中描述一个事物需要使用class修饰的类来完成,在Scala中需要执行代码即描述对象的执行过程时需要使用object修饰的类来完成,Scala中只有object修饰类才能写main方法即执行代码。
class Monster(val name: String, var HP: Int, val ATK: Int) extends Fighter {
def combat(list: List[Fighter]): Unit = {
val fighter: Fighter = ATKwith(list)
fighter.beAttacked(ATK)
println(this.name + "\t普攻\t" + fighter.name)
}
}
object Monster {
def apply(name: String, HP: Int, ATK: Int): Monster = new Monster(name, HP, ATK)
}
// 若想使属性只能在当前类访问可以这么写
private[this] val cardID = "123456"
class Ponit {
private var _x = 0 // 只能在当前了中或伴生对象中进行访问
// 获取属性 --> 提供 getter方法
def x = _x
// 赋值属性 --> 提供setter方法 setter方法名字和getter方法名字类似 所以为了区分
// 建议setter方法的方法名字 后面添加_
def x_(x:Int):Unit = {
_x = x
}
}
@BeanProperty使用了和Java完全相似getter和setter,但是,使用这种方式不能私有化。
import scala.beans.BeanProperty
class Person {
// 默认和Java中提供的getter和setter是一致的了,不能使用private
// 这个下划线是占位符
@BeanProperty var name:String = _
}
Scala中没有构造方法重载的概念,为了达到平时对不同属性调用不同构造方法进行初始的方式,scala提供了辅助构造方法
class 类名(参数列表){ --> 主构造方法(只能有一个)
def this(参数列表){ --> 辅助构造方法
}
}
主构造方法只能写一个,所以一般主构造方法只写无参,其他的使用辅助构造方法写,这样就和Java基本一样。
// 主构造方法中的修饰
private var 属性:数据类型 // 当前属性只能在当前类访问
var 属性:数据类型 // 相当于是共有的getter和setter方法,但是属性是私有化
val 属性:数据类型 // 相当于提供了getter方法,但是不能赋值
主构造方法中的参数可以直接当做属性来使用。
辅助构造方法的方法名必须是this,辅助构造方法可以有多个,根据不同参数进行区分。
class Constuctor2(var name:String) {
var age:Int = _
//在创建对象的时候,将age属性也进行初始化
def this(age:Int,name:String){
//辅助构造方法代码中第一句必须调用 主构造方法 不能将辅助构造方法和主构造声明成一个样式
this(name)
this.age = age
}
}
class 类名 extends 特质1 with 特质2 with 特质3 …
class 类名 extends 父类 with 特质1 with 特质2 …
若需要继承类,还要继承特质,一定先继承类,然后再实现特质。
特质可以有属性、抽象方法、实现方法。
trait Flyable {
// 1.特质中属性的定义
val speed:Int = 1000
// 2.可以声明一个没有值的属性 ,当类继承与特质的时候,必须给当前属性进行赋值
// 这样属性可当做抽象属性看待
val height:Int
// 3.定义实现的方法
def fly :String = {
"I Can fly"
}
// 4.定义抽象方法 --> 不写方法体即可
def fight:String
}
class Brid extends Flyable{
//需要实现特质中没有实现的属性和方法,必须添加override关键字
override val height: Int = 100
override def fight: String = "战斗"
//已经在特质中实现方法再次重新实现 (scala中将特质其实就是看做是一个 类 父类)
override def fly: String = "不想飞了"
}
Scala中特质允许继承类。
特质会获取类中属性和行为。
类前必须有abstract关键字,必须使用class修饰。
两个类在同一个文件中,两个类的名字必须相同,其中一个类使用class修饰,另外一个类使用object修饰,这样就会被称为伴生类,,这个伴生类指的是object修饰的类,而通过object类创建出来的对象,叫做伴生对象。
伴生类的出现就是为了解决封装问题,scala中伴生类可以访问原有类的私有属性。伴生类和原生类之间是可以互相访问私有属性,普通属性和方法也是完全可以的,但是若原生类使用private[this],伴生类无法访问。
因为伴生类也可以创建对象,所有伴生类中提供一个方法apply,多用于伴生类创建对象。每个伴生类都是默认提供一个apply方法,这个方法就是伴生类创建对象的方法。我们可以定义apply对原生类中提供属性赋值操作(重载)。apply方法构建对象时候就会自动调用。
单继承,主构造方法可以调用父类的主构造方法,默认无参,子类的辅助构造方法不能调用父类的构造方法。
isInstanceOf:判断对象是否属于指定类
asInstanceOf:进行类型转换
Scala中没有Java中的switch-case语句,但是Scala中有更强大的匹配模式。
匹配对象(任何数据类型) match{
case 表达式/值/方法/类 => 代码
case 表达式/值/方法/类 => 代码
case _ => 代码
}
下换线当做通配符,通配任意可能。
//标准模式
val ch: Char = 'p'
val chp = 'p'
ch match {
case '+' => println("加法")
case '*' | '%' => println("|表示的是或者关系")
case chp => println("匹配成功")
case _ => println("最后一种情况[相当于剩余所有情况]")
// 允许添加条件守卫
// case _ if(条件) => 执行代码
}
// 匹配其他数据
// 1.匹配数组(取出了元素值)
val arr = Array("scala", 1, 2.0, 'a')
// match-case不仅可以匹配值,还可以匹配数据类型
val value: Any = arr(Random.nextInt(4))
value match {
case x: Int => println("当前是Int类型" + x) // x数据是一个局部变量,只能在match中case内使用
case s: String => println("当前是String类型" + s)
case d: Double => println("当前是Double类型" + d)
case _ => println("字符")
}
// 直接匹配类型 --> 相当于匹配是否是当前类型的对象
value match {
case Char => println("Char类型的对象") // 匹配value是否是Char类型的对象
case _ => println("匹配的是数据类型")
}
// 匹配Map集合
val map = Map(("1", 1), ("2", 2))
map match {
case m: Map[String, Int] => println("存储着String和Int类型数据")
case m: Map[_, _] => println("所有Map都可以匹配,相当于是所有类型剩余的情况")
}
// 匹配数组
val arr1 = Array(1, 1)
val res = arr1 match { // 可以接收匹配成功后的值
// 匹配数组中存储的元素
case Array(0) => "0" // 数组中是否存在0这个元素
case Array(x, y) => (x + y) + "" // 只要数组中存在两个元素,就可以赋值到x和y的身上
case Array(0, _*) => "0..." // 匹配数组是否是从0开始 -->数组是否是以0开头 _*代表剩余的元素
case _ => "something"
}
println(res)
// 匹配字符串
val arr2 = Array("zhangsan","lisi")
val name = arr2(Random.nextInt(arr2.length))
name match{
case "zhangsan" => println("张三") // 值
case s:String => println(s) // 匹配类型,千万不要把类型放到值的上面,因为只要是String类型都会被匹配走
case _ => println("所有")
}
// 列表List
val list = List(1,2)
list match{
case 0 :: Nil => "0" // 集合中是否是以0开头
case x :: y :: Nil => x+" "+y// 当前集合时候包含连续元素
case 0 :: tail => "0.." // 以0开头的元素
case _ => "剩余情况"
}
// 同理可以使用下标取值匹配 --> 参考Array
// 使用比较高 元组
val pair = (1,2)
pair match{
//case pair._1 => println("") // 相当于是匹配的整个元组即 (x,y) 这样的形式才可以
case (0,_) => "0..."// 元组中是否存在0 ,并且在第一个元素的位置
case (x,y) => (x+y) +"x+y" // 匹配元组中元素 x和y都可以进行操作
case (_,_) => "匹配任何元素 这个下划线的个数决定这元组存储数据的个数"
case _ => "任意类型"
}
// ps:单独匹配元组中的值,那么就现将值取出后在进行匹配
pair._1 match{
case x:Int => x
}
case class 类名(构造方法) { 写类的实现}
case object 类名 --> 作用只有一个就是做模式匹配
import scala.util.Random
// 当前样例类对数据进行分装传递
object CaseCalssDemo {
def main(args: Array[String]): Unit = {
val arr =
Array(CheckTimeOutTask,SubmitTask("0001","task_001"),HeatBeat(15000))
val obj = arr(Random.nextInt(arr.length)) // 获取存储在Array数组中是三个对象(随机获取)
obj match{
case CheckTimeOutTask => println("任务超时")
// 匹配成功存储在SubmitTask对象中属性就会被赋值到id 和task 变量上
case SubmitTask(id,task) => println("任务ID"+id+"任务名称:"+task)
case HeatBeat(time) => println("时间:"+time)
case _ => println("剩余")
}
}
}
// 每一个样例类都会有自己的apply,所以直接获取类名的方式就可以创建对象
// 开发会将样例类单独存到一个文件,通过对这个文件的维护,就可以添加和删除或修改对应样例类
case object CheckTimeOutTask
case class SubmitTask(id:String,taskName:String)
case class HeatBeat(time:Long)
是一种特殊的匹配模式,不使用match关键字,但是有多组case语句组成,这样的形式就是偏函数。
偏函数有一个名字 PartialFunction[A,B], A代表的是参数类型,B代表的是返回值类型。
def f1:PartialFunction[String,Int]={
case "one" => 1
case "two" => 2
case _ => -1
}
val i = f1("two")
方法可以定义多个参数列表,当使用较少的参数列表调用多参数列表的方法时,会产生一个新的函数,该函数接收剩余的参数列表作为其参数。这被称为柯里化。
柯里是个人名,柯里化是指将一个以函数为返回值的函数调用的简化。
val f1 = (s:String)=>{
println(s)
(i:Int)=>{
println(s + i)
}
}
// 这个时候想调用f1返回值的这个函数就需要这么写
f1("s1")(1)
// 这样就可以获取f1的返回值,是个函数
val f2 = f1("s2")
// 柯里化就是简化这个函数
val f3 = (s:String)=>(i:Int)=>{
println(s)
println(s + i)
}
f3("f3")(0)
函数内部调用一个或多个外部变量,这个变量并不存在函数的内部,这个就是闭包。
同Java中的闭包,都是提升外部变量的声明周期实现闭包。
// 引用外部变量,这就是闭包
val a = 2
val f1 = (i:Int)=>{
i * a
}
关键字:implicit
在不显示调用某个方法的前提下,就可以完成对数据的其他形式操作,这样方式其实就是隐式转换。
隐式转换一直在用,Scala中隐式转换经常被用到。
当写Scala程序的时候,会自动为类添加三个隐式转换包:
import java.lang._
import scala._
import Predef._
// 定义一个隐式转化类
implicit class RichFile(from:File){
def read:String = Source.fromFile(from.getPath).mkString
}
隐式参数在柯里化的时候大量使用,多是系统提供的方法,高阶函数。
trait Adder[T]{
def add(x:T,y:T):T
}
implicit val a = new Adder[Int] {
override def add(x: Int, y: Int): Int = x+y
}
def addTest(x:Int,y:Int)(implicit adder: Adder[Int])={
adder.add(x,y)
}
[B <: A] 上界或上限
[B >: A] 下界或下限
[B <% A] 视界
[B: A] 上下文界
[-A] 逆变
[+B] 协变
必须存在一个隐私转换
class Pair_Context[T:Ordering](val first:T,val second:T){
def smaller(implicit ord:Ordering[T])={
if(ord.compare(first,second) < 0) first else second
}
}
object Demo{
def main(args: Array[String]): Unit = {
val i = new Pair_Context(1,2)
println(i.smaller)
}
}
/ 柯里化就是简化这个函数
val f3 = (s:String)=>(i:Int)=>{
println(s)
println(s + i)
}
f3(“f3”)(0)
# 闭包
函数内部调用一个或多个外部变量,这个变量并不存在函数的内部,这个就是闭包。
同Java中的闭包,都是提升外部变量的声明周期实现闭包。
```scala
// 引用外部变量,这就是闭包
val a = 2
val f1 = (i:Int)=>{
i * a
}
关键字:implicit
在不显示调用某个方法的前提下,就可以完成对数据的其他形式操作,这样方式其实就是隐式转换。
隐式转换一直在用,Scala中隐式转换经常被用到。
当写Scala程序的时候,会自动为类添加三个隐式转换包:
import java.lang._
import scala._
import Predef._
// 定义一个隐式转化类
implicit class RichFile(from:File){
def read:String = Source.fromFile(from.getPath).mkString
}
隐式参数在柯里化的时候大量使用,多是系统提供的方法,高阶函数。
trait Adder[T]{
def add(x:T,y:T):T
}
implicit val a = new Adder[Int] {
override def add(x: Int, y: Int): Int = x+y
}
def addTest(x:Int,y:Int)(implicit adder: Adder[Int])={
adder.add(x,y)
}
[B <: A] 上界或上限
[B >: A] 下界或下限
[B <% A] 视界
[B: A] 上下文界
[-A] 逆变
[+B] 协变
必须存在一个隐私转换
class Pair_Context[T:Ordering](val first:T,val second:T){
def smaller(implicit ord:Ordering[T])={
if(ord.compare(first,second) < 0) first else second
}
}
object Demo{
def main(args: Array[String]): Unit = {
val i = new Pair_Context(1,2)
println(i.smaller)
}
}