scala 知识点总结
Posted BigData Scholar
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了scala 知识点总结相关的知识,希望对你有一定的参考价值。
前言:万字长文总结scala知识点,适用于了解Java或有一定其他编程语言基础的同学,过于简单的本文未记录。
0.Hello World
废话不多说,从最简单的hello world和print开始吧。
Hello World
object Scala01_HelloWorld {
//scala源码中包含main函数,在编译后自动形成了public static void main
//编译源码会生成两个字节码文件,静态main方法执行另一个字节码文件中的成员main方法
//scala是完全面向对象语言,没有静态语法,只能通过模拟生成静态方法
//编译时将当前类生成一个特殊类==>Scala01_HelloWorld$,然后创建对象来调用这个对象的main方法
//一般情况下,将加$的类的对象,称之为"伴生对象",伴生对象的内容可以通过类名访问,来模拟java的静态方法
//伴生对象的语法规则:使用object声明
//scala没有public关键字,默认都是公共的
//scala没有void关键字,采用特殊对象Unit模拟,Unit唯一实例是()
//声明方法采用def
//方法后面的()是方法的参数列表,java:String 参数名;scala:参数名:类型
//方法的声明和方法体{}是通过=连接
//方法的返回值类型放在方法声明的后面,使用:连接
def main(args: Array[String]): Unit = {
println("hello world")
}
}
Print
//打印字符串
object Scala02_StringPrint {
def main(args: Array[String]): Unit = {
val name = "woodie"
val age = 20
//类java,+拼接
println("name=" + name + " age=" + age)
//类c,printf
printf("name=%s, age=%d \n", name, age)
//插值字符串
println(s"name=${name},age=${age}")
println(f"name=${name},age=${age}%.2f") //f格式化
println(raw"name=${name},age=${age}%.2f \n") //raw原始值 \n直接打印出来
}
}
1.变量
-
定义变量关键字:var/val
-
var name:String = "woodie" //关键字 变量名:[数据类型] = 变量值(变量必须初始化或者用_让系统赋默认值)
-
类型都是对象,没有原始类型
-
数据类型分为两大类 AnyVal(值类型) 和 AnyRef(引用类型)
-
强制转换调用方法:toInt/toDouble等
-
代码练习
/**
* 变量
*/
object Scala01_Var {
//在方法外部声明的val变量,等于java final修饰
val gender: String = "female"
def main(args: Array[String]): Unit = {
//java中变量使用之前必须初始化,可以先声明后赋值,scala不允许
//var 变量名称: 变量类型 = 变量值,变量类型可以省略(自动推断)
var name: String = "woodie"
//没有基本数据类型,完全面向对象语言
var c: Char = 'c'
var age: Int = 10
var flag: Boolean = true
val userName = "woodie"
//val 修饰的变量值不可改变
val name1: String = "woodie"
var dog = new Dog();
dog.name = "xxx"
println(dog.name)
//数据类型分为AnyVal(值类型)和AnyRef(引用类型)两大类
//AnyRef:scala collections/scala classes/java classes/Null/Nothing
//类型转换
val i = 10;
val d = i.toDouble
println(i.toChar)
//Byte Short运算时会先转成Int
//没有++ --操作
val b: Byte = 5
//b = b - 2 //错误
println("12.5".toDouble.toInt)
//标识符:可以用特殊符号作为标识符,必须为首字符,编译时进行了转换,如+ --> $plus
//_有特殊用途,不能单独当变量名
val ++ = "123"
println(++)
//反引号
val `val` = 'c'
}
//表示不正常的返回
def f1(): Nothing = {
throw new Exception()
}
}
class Dog {
var name: String = ""
}
2.运算符
-
没有三目运算符
-
位运算(-3 & -2)
-
二进制的最高位是符号位: 0表示正数,1表示负数 -
所有的运算都是以二进制补码进行. -
非负数的原码,反码,补码都一样 -
负数的反码=它的原码符号位不变,其它位取反(0->1,1->0) -
负数的补码=它的反码+1 -
在计算机运算的时候,都是以补码的方式来运算的,但是返回结果时,其实会将补码转成原码 -
键盘输入:Stdin.readline/Stdin.readInt等
3.程序流程控制
-
没有switch分支,有match-case
-
if/else
-
返回值为满足条件的最后一行代码
-
for循环
-
基本格式
for(i <- 0 to 10)
for(i <- 0 until 10)
for(item <- List(1,2,4)) -
循环守卫
for(i <- 0 to 10 if i%2==0)
-
引入变量
for(i <- 1 to 3; j = 4 - i)
-
嵌套循环
//处理简单逻辑
for(i <- 1 to 3; j <- 1 to 3)
//处理复杂逻辑按java嵌套方式 -
循环返回值
//使用yield返回集合,yield后可跟代码块
val res = for(i <- 1 to 10) yield i * 2 -
循环条件可以使用{}替代()
for{i <- 1 to 10}
-
控制步长
for(i <- Range(0,10,2))
-
while
-
返回值为unit -
循环中断
-
import util.control.Breaks._ 用breakable代码块包住循环体后再使用break中断:breakable{循环}
代码练习
import scala.util.control.Breaks
object Scala03_ProcessControl {
def main(args: Array[String]): Unit = {
//TODO Scala任何表达式都有值
var a = 1
println(a = 1)
val value: Any = if (a == 1) {
"xxx"
}
println(value)
//TODO for 循环
//to []前后闭合 until [) 前闭后开
//0 until3 == 0.until(5) 完全面向对象,until源码实际是Range
for (i <- 1 to 5) {
println(s"to --> ${i}")
}
for (i <- 1 until 5) {
println(s"until --> ${i}")
}
for (i <- Range(1, 5)) {
println(s"Range --> ${i}")
}
//Range可设置步长
for (i <- Range(1, 5, 2)) {
println(s"Range step --> ${i}")
}
//杨辉三角
println("杨辉三角:")
for (i <- Range(1,18,2)){
println(" "*((18-i)/2) + "*"*i + " "*((18-i)/2))
}
// TODO 循环守卫:增加判断条件,相当于continue
for (i <- 1 to 5 if i%2==0){
println(i)
}
println("杨辉三角2:")
for (i <- Range(1,18,2); j = (18-i)/2){
println(" "*j + "*"*i + " "*j)
}
//for 后的()可换成{},条件可换行
println("杨辉三角3:")
for {i <- Range(1,18,2)
j = (18-i)/2 }
{
println(" "*j + "*"*i + " "*j)
}
//嵌套循环可放到一个循环
for(i <- 1 to 3; j <- 1 to 3){
println(i,j)
}
//TODO for 循环返回值 默认是(),使用yield将遍历过程中处理的结果返回到一个vector集合中
val res = for (i <- 1 to 10) yield i
println(res)
//TODO 中断循环,java:break;scala没有break关键字,可以采用对象的方式中断:Breaks
println("Breaks:")
Breaks.breakable{ //breakable相当于try catch
for (i <- 1 to 10) {
if (i == 5) {
Breaks.break() //中断程序
}
println(i)
}
}
//TODO 乘法表
println("乘法表:")
for (i <- 1 to 9){
for (j <- 1 to i){
print(s"${j} * ${i} = "+j*i + " ")
}
println()
}
}
}
4.函数式编程
4.1 基础部分
-
定义
-
方法是在类/接口中定义的,依赖类和对象 -
函数不用依赖于类和对象,没有重载概念 -
方法与函数 -
基本语法
-
def 函数名 ([参数名: 参数类型], ...)[[: 返回值类型] =] {return 返回值}//return可省略
-
调用原则
-
当程序运行到函数/方法时,就会开辟一个独立栈 -
当一个栈遇到return或执行完毕,就会返回调用的位置 -
注意事项与细节
-
没有形参调用时可不带()
-
return和返回值类型可省略:函数可以根据函数体最后一行代码自行推断函数返回值类型
-
如果明确写了return,就不能使用自行推断了,返回值类型也需要明确写出
-
如果返回值类型声明为unit,就算也return也不会返回值
-
如果不确定返回值类型,可省略
-
函数中还可以嵌套函数,scala任何语法结构都可以嵌套其他语法结构
-
省略规则
-
最后一行是返回值,return可省略
-
没有形参,()可省略
-
没有返回值,=可省略
-
只有一行代码,{}可省略
-
匿名函数
()->println("hello")
-
没有返回值的函数为过程
-
可变参数,使用*
def test(name: String*) = {}
-
惰性函数
-
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行
//不执行sum函数println("res=" + res) //当需要使用到res时,就会真正的开始计算
lazy val res = sum(1,2) -
异常
-
catch中用case捕捉异常
case ex: Exception => {}
-
throw关键字抛出异常
throw new Exception("不对")
4.2 高级部分
-
偏函数
-
使用构建特质的实现类(使用的方式是PartialFunction的匿名子类)
-
PartialFunction 是个特质(看源码)
-
构建偏函数时,参数形式 [Any, Int]是泛型,第一个表示传入参数类型,第二个表示返回参数
-
当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt()如果为true ,就会执行 apply, 构建一个新的Int 对象返回
-
执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象.
-
map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素
-
collect函数支持偏函数
val addOne = new PartialFunction[Any, Int] {
override def isDefinedAt(x: Any): Boolean = {
x.isInstanceOf[Int]
}
override def apply(v1: Any): Int = {
v1.asInstanceOf[Int] + 1
}
}
val array = Array(1, 2, 3, "hello")
println(array.collect(addOne).mkString(",")) //2,3,4
//简写1
def addOne1():PartialFunction[Any,Int] = {
case i:Int => i + 1
}
println(array.collect(addOne1()).mkString(",")) //2,3,4
//简写2
println(array.collect { case i: Int => i + 1 }.mkString(",")) //2,3,4 -
作为参数的函数
-
函数作为变量传入到另一个函数
def plus(x: Int) = 1 + x
val result1 = Array(1, 2, 3, 4).map(plus) //plus是作为参数的函数,map是高阶函数 -
匿名函数
-
没有名字的函数
(x:Int) = x*x
-
高阶函数
-
能够接受函数作为参数的函数,或者可以返回一个匿名函数
//接受函数作为参数
def test(f: Double => Double, n1: Double) = {
f(n1) //调用f函数
}
def sum(d: Double): Double = {
d + d
}
val res = test(sum, 6.0)//返回匿名函数
def sum(x:Int)={
(y:Int) => x+y
}
sum(10)(20) -
参数类型推断
-
参数类型是可以推断时,可以省略参数类型
val list = List(1, 2, 3, 4)
println(list.map((x: Int) => x + 1)) // List(2,3,4,5)
//1. 因为类型可以推断,因此可以省略Int
println(list.map((x) => x + 1))
//2. 因为形参只有一个,() 可以省略
println(list.map(x => x + 1))
//3.因为x 在 => 右边只出现一次,因此 使用 _ 替代
println(list.map(_ + 1)) -
闭包
-
一个函数和与其相关的引用环境(变量/值)组合的一个整体(实体),闭包因为可以保留上次引用的某个值,所以我们传入一次就可以反复使用 def main(args: Array[String]): Unit = {
//使用
val f = makeSuffix(".jpg")
println(f("小猫.jpg")) // 小猫.jpg
println(f("小狗")) // 小狗.jpg
}
/*
1) 编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包
2) 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀,则返回原文件名。
*/
def makeSuffix(suffix: String) = {
(fileName:String) => {
if (fileName.endsWith(suffix)) fileName else fileName + suffix
}
} -
柯里化
-
接受多个参数的函数都可以转化为接受单个参数的函数
//传统
def mul(x: Int, y: Int) = x * y
println(mul(10, 10))
//闭包
def mulCurry(x: Int) = (y: Int) => x * y
println(mulCurry(10)(9))
//柯里化
def mulCurry2(x: Int)(y:Int) = x * y
println(mulCurry2(10)(8)) -
控制抽象
-
参数是函数 -
函数参数没有输入值也没有返回值 -
将一段代码(从形式上看),作为参数传递给高阶函数,如breakable。
def main(args: Array[String]): Unit = {
introduce(println("I am woodie"))
introduce{
println("I am woodie")
println("I am paul")
}
}
def introduce(block : =>Unit){
println("hi")
block
println("bye")
}
5.面向对象编程
5.1 基础部分
-
定义
-
类并不声明为public,默认为public -
属性必须要有初始值,可以使用下划线_让系统分配默认值,var name:String = _ -
构造方法:包括主构造器一个和辅助构造器多个(重载)
-
主构造器
-
主构造器放置于类名后:主构造器会执行类定义中的所有语句
class Person(pName: String){var name = pName}
-
属性会在底层自动生成getter和setter
-
私有主构造方法:
class C private()
-
构造器参数
-
主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量 -
使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用 -
如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,这时成员属性是私有的,但是可读写 -
Bean属性
给某个属性加入@BeanPropetry注解后,会生成getXXX和setXXX的方法:
@BeanProperty var name:String = "woodie"
-
辅构造器
所有辅构造器名称为this,必须调用主构造器且必须放在第一行:
def this(name : String) {
this()
this.name = name
}-
创建对象
-
默认使用val创建对象,一般来说只改变对象的属性,而不是改变对象的引用
-
创建对象时类型一般使用自行推断,多态情况下需写明类型
-
内存分配机制:对象的引用存在栈区,对象的属性存在堆区
-
流程分析
-
加载类的信息(属性信息和方法信息) -
在内存中(堆)给对象开辟空间 -
使用父类的构造器(主构造器/辅助构造器)完成父类的初始化 (多个父类) -
使用本类的主构造器完成初始化( age = 90 name = "null") -
使用本类的辅助构造器继续初始化(age =20,name = "小倩") -
将对象在内存中的地址赋给 p 这个引用
5.2 中级部分
-
包
-
可以嵌套使用 -
作用域原则:可以直接向上访问 -
父包要访问子包的内容时,需import对应的类 -
包对象
-
为了弥补包中不能直接定义函数/方法 和 变量的不足,scala提供包对象的概念来解决 -
在底层包对象(package object scala) 会生成两个类 class package class package$ -
引入包
-
import可以出现在任何地方 -
使用_导入包中所有类,java是*,import scala.collection.mutable._ -
选择器:只选择包中几个类,使用{},import scala.collection.mutable.{HashMap,HashSet} -
使用=>重命名类:import java.util.{ HashMap=>JavaHashMap, List} -
使用=>_隐藏类:import java.util.{ HashMap=>_, _} // 含义为 引入java.util包的所有类,但是忽略 HahsMap类 -
面向对象三大特征
-
封装 -
与java类似,自动生成属性的getter/setter
-
继承
-
与java类似,子类继承父类所有属性(私有属性通过公用方法访问)
-
重写方法和复写字段需要override关键字
-
调用超类使用super
-
isInstanceOf判断是否属于某类/asInstanceOf转换成子类的引用
-
超类构造
-
只有主构造器才可以调用超类构造,不能显示使用super
-
抽象类
-
abstract修饰类,方法省去方法体,字段不赋值 -
抽象方法和属性不能用private和final -
抽象类中可以有实现的方法 -
多态
5.3 高级部分
-
伴生类/伴生对象(java静态)
-
如果我们在同一个文件中,写了 class Person 和 object Person,就把 class Person 称为 伴生类, object Person 称为伴生对象 -
如果我们在同一个文件中,只写了 class Person ,那么Person就是一个普通的类 -
如果我们在同一个文件中,只写了 object Person, 那么在底层就会自动生成对应的伴生类 class Person, 只是这个伴生类是空 -
伴生对象,可以访问到伴生类的任何方法和属性 -
apply方法:类名(参数) 方式来创建对象实例,可以重载 -
单例模式
-
概念:保证在整个的软件系统中,某个类只能存在一个对象实例
-
实例 -
伴生类主构造方法设为私有: class SingleTon2 private()
伴生对象实现单例方法:
object SingleTon {
private var s: SingleTon = null
//懒汉式的单例
def getInstance = {
if (s == null) {
s = new SingleTon
}
s
}
}
object SingleTon2 {
private val s: SingleTon2 = new SingleTon2
//饿汉式
def getInstance = {
s
}
}-
特质(接口):对接口和单继承的补充,scala只能有一个父类
-
声明:
trait 特质名 {
trait体
} -
使用:extends with
-
可以同时拥有抽象和具体方法,java接口可以当做特质使用
-
混入特质:
val oracleDB = new OracleDB with Operate3 {
override def insert2(): Unit = {
println("insert2")
}
} -
叠加特质
-
概念:构建对象时同时混入多个特质 -
顺序:方法执行顺序从右到左,特质声明顺序从左到右,执行顺序从右到左 -
特质super并不是调用父特质,而是向左查找特质 -
调用具体特质:super[特质].xxx() -
特质的字段不是继承,而是变成类的字段
-
特质可以继承类,并变成该类超类的子类
-
嵌套类(类似java内部类)
6.隐式转换
6.1 隐式函数
implicit def f(d:Double): Int = { //底层会生成一个方法 f$1
d.toInt
}
val n: Int = 3.4 //=> val n: Int = f$1(3.4)
-
隐式函数与函数名称无关 -
用途:满足OCP原则,通过隐式函数添加功能
6.2 隐式值
-
隐式值也叫隐式变量,将某个形参变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺省参数
def main(args: Array[String]): Unit = {
//str1 隐式值
implicit val str1: String = "jack"
//1.implicit name: String 隐式参数
//2.当调用hello的时候,没有传入实参,则编译器会自动的将隐式值关联到name上
def hello(implicit name: String): Unit = {
println(name + " hello")
}
hello //用到隐式值 底层 hello$1(str1)
}
6.3 隐式类
-
构造参数有且只能有一个
-
不能是top-level
-
不能是case class
-
作用域内不能有与之相同名称的标识符
object ImplicitClass {
def main(args: Array[String]): Unit = {
//一个隐式类, 可以返回一个隐式类的实例,然后就可以调用隐式类的方法
implicit class DB1(val m: mysql1) {
def addSuffix(): String = { //方法
m + " scala"
}
def sayHi(): Unit = {
println("sayHi..")
}
def sayHello(): Unit = {
println("hello")
m.sayOk()
}
}
val mySQL = new MySQL1
mySQL.sayOk() //
// 1.底层 DB1$1(mySQL).addSuffix()
// 2. DB1$1(mySQL) 返回的是 :ImplicitClass$DB1$2 实例
// 3. 通过返回的 ImplicitClass$DB1$2实例.addSuffix()
println(mySQL.addSuffix()) //DB1$1(mySQL).addSuffix()
mySQL.sayHi()
mySQL.sayHello()
}
}
class MySQL1 { //普通类
def sayOk(): Unit = {
println("sayOk")
}
}
6.4 隐式转换前提
-
不能存在二义性 -
不能嵌套
7.数据结构
7.1 集合
7.1.1 特点
-
所有集合都扩展子Iterable特征,分为可变(mutable,如数组)和不可变(immutable,如Arraylist)两种类型
-
immutable
-
mutable
7.1.2 分类
-
Seq序列 -
Map映射 -
Set集
7.1.3 数组
-
定长数组
-
定义方式
new Array[Int](10) //等同java int[]
Array(1,2,3) //apply方法创建 -
取值/赋值
arr(0) //0下标
-
变长数组
-
定义方式
val arr = ArrayBuffer[Int]()//类似java Arraylist
-
追加元素
arr.append(0)
-
重新赋值
arr(0)=1
-
删除元素
arr.remove(0) //表示删除下标为0的
-
定长与变长转换
toBuffer() //定长转变长
toArray() //变长转定长 -
多维数组
-
定义方式
Array.ofDim[Int](3,4)
Array(Array(1,2), Array(4,5,6), Array("hello","北京")) -
scala数组与java数组转换
-
scala数组转java list
// Scala集合和Java集合互相转换
val arr = ArrayBuffer("1", "2", "3")
import scala.collection.JavaConversions.bufferAsJavaList
//即这里的bufferAsJavaList是一个隐式函数
/*
implicit def bufferAsJavaList[A](b : scala.collection.mutable.Buffer[A]) : java.util.List[
*/
val javaArr = new ProcessBuilder(arr)
val arrList = javaArr.command()
println(arrList) //输出 [1, 2, 3] -
java list转scala buffer
//asScalaBuffer 是一个隐式转换
/*
implicit def asScalaBuffer[A](l : java.util.List[A]) : scala.collection.mutable.Buffer[A]
*/
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
scalaArr.append("jack")
println(scalaArr)
7.1.4 元组
-
理解为一个容器,可以存放不同类型的数据
-
最多不能超过22个元素
-
基本操作
-
创建:
val tuple = (1, 2, "hello")
-
访问
tuple._1 //下标从1开始
tuple.productElement(2) //下标从0开始 -
遍历
for (i <- tuple.productIterator)
7.1.5 列表
-
List
-
与java List不一样,java List是接口,scala List是object,属于Seq
-
默认不可改变
-
基本操作
-
创建
val list = List(1,2,3)
Nil //空集合 -
访问
list(0)
-
追加
-
列表最后增加:list:+4 -
列表最前增加:4+:list //:挨着集合,+挨着元素 -
:: //表示向集合添加新元素,集合一定要放在最右边,不产生新的集合 -
::: //将集合的每一个元素加到里另一个集合,左右两边都必须是集合 -
删除
drop(1) //下标从1开始
-
ListBuffer
-
可变list集合,属于Seq
-
基本操作
-
创建
ListBuffer[Int](1,2,3)
-
访问
list(0)
-
追加
list+=4
list.append(4)
list0++list1 //加入集合中的各个元素
list:+4 //单独加入元素 -
删除
remove(0) //下标从0开始
7.1.6 队列Queue
-
说明
-
有序列表,在底层可以用数组或是链表来实现。 -
其输入和输出要遵循FIFO -
分为可变不可变 -
基本操作
-
创建
val q = new mutable.Queue[Int]
-
追加
q += 1
q ++= List(1,2,3)
q.enqueue(1,2,3) -
删除
val ele = q.dequeue() //取出并删除头元素
-
读取
q.head
q.last
q.tail //返回除第一个以外的元素
7.1.7 映射Map
-
不可变Map是有序的,可变的Map是无序的
-
基本操作
-
创建
-
不可变:
val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> "北京")
-
可变:
val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
-
空Map:
val map3 = new scala.collection.mutable.HashMap[String, Int]
-
对偶元组:
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30) )
-
读取
map(key)
contains(key)
map.get(key).get //get(key)返回Option对象,要么是Some,要么是None
map.getOrElse("A","默认值") -
修改&增加
map("A")=1
map += ("A"->1,"B"->2) -
删除
remove(key)
map -= ("A","B") -
遍历
for ((k, v) <- map1) println(k + " is mapped to " + v)
for (v <- map1.keys) println(v) //keys
for (v <- map1.values) println(v) //value
for(v <- map1) println(v) //v是Tuple2
7.1.8 集Set
-
默认使用不可变
-
基本操作
-
创建
Set(1,2,3,"a")
mutable.Set(1,2,3,"a") -
添加
set.add(4)
set += 4
set.+=(4) //等同于set += 4 ,+号是方法 -
可变集删除
set -= 4
set.-=(4)//等同于set -= 4 ,-号是方法
set.remove(4)
7.2 集合应用操作:高阶函数(核心重点)
7.2.1 map映射
-
将集合中的每一个元素通过指定功能(函数)映射(转换)成新的 结果集合这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点
/**
map demo:名称转为全大写
*/
object Scala05_map {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
val names2 = names.map(upperName)
println(names2) //List(ALICE, BOB, NICK)
}
def upperName(name:String):String = {
name.toUpperCase
}
}
7.2.2 flatMap扁平化
-
将集合中的每个元素的子元素映射到某个函数并返回新的集合
/**
flatMap demo:名称转为全大写
*/
object Scala05_map {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
val names2 = names.map(upperName)
println(names.flatMap(upperName)) //List(A, L, I, C, E, B, O, B, N, I, C, K)
}
def upperName(name:String):String = {
name.toUpperCase
}
}
7.2.3 filter过滤
-
将符合要求的数据(筛选)放置到新的集合中
/**
filter demo:过滤出名称开始为A的
*/
object Scala05_map {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
val names2 = names.map(upperName)
println(names.filter(startA)) //List(Alice)
}
def startA(name:String)={
if(name.startsWith("A"))
true
else
false
}
}
7.2.4 reduce/reduceLeft/reduceRight化简
-
将二元函数引用于集合中的函数
/**
* 化简 demo:累减
*/
object Scala05_Collect_HighLevleFunc {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5)
println(list.reduce(minus)) //-13
println(list.reduceLeft(minus)) //-13
println(list.reduceRight(minus)) //3
}
def minus(n1:Int,n2:Int)={
n1-n2
}
}
7.2.5 fold/foldLeft/foldRight折叠
-
将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历,运用到函数柯里化
-
reduceLeft就是调用的foldLeft[B](head),并且是默认从集合的head元素开始操作的
/**
折叠 demo:累减
*/
object Scala05_Collect_HighLevleFunc {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5)
println(list.fold(10)(minus)) //-5
println(list.foldLeft(10)(minus)) //-5
println(list.foldRight(10)(minus)) //-7
println((10 /: list)(minus)) //foldLeft 简写
println((list :\ 10)(minus)) //foldRight 简写
}
def minus(n1:Int,n2:Int)={
n1-n2
}
}/**
* 统计每个字母出现次数
*/
import scala.collection.mutable
object Scala06_Collect_CharCount {
def main(args: Array[String]): Unit = {
val sentence = "AAAAAABBBBACCCSSSDDDDEE"
val map = mutable.Map[Char, Int]()
val map1 = sentence.foldLeft(map)(charCount)
println(map1.toArray.sortBy{case (_,n)=>n}.reverse.mkString(","))
}
def charCount(map:mutable.Map[Char,Int],c:Char):mutable.Map[Char,Int]={
map += ((c,map.getOrElse(c,0)+1))
}
}
7.2.6 scan扫描
-
对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存
/**
* 扫描 demo:累减
*/
object Scala05_Collect_HighLevleFunc {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4, 5)
println(list.scan(10)(minus)) //List(10, 9, 7, 4, 0, -5)
println(list.scanLeft(10)(minus)) //List(10, 9, 7, 4, 0, -5)
println(list.scanRight(10)(minus)) //List(-7, 8, -6, 9, -5, 10)
}
def minus(n1:Int,n2:Int)={
n1-n2
}
}
7.2.7 zip拉链
-
将两个集合进行对偶元组合并
object Scala06_Collect_Other {
def main(args: Array[String]): Unit = {
//拉链的基本使用-合并
val list = List("no1", "no2", "no3") // 文件
val list2 = List("北京", "上海", "广州") // 数据库
val list3 = list zip list2
println("list3=" + list3) //list3=List((no1,北京), (no2,上海), (no3,广州))
}
}
7.2.8 iterator迭代器
-
通过iterator方法从集合获得一个迭代器,对集合进行遍历
val iterator = List(1, 2, 3, 4, 5).iterator // 获取到迭代器 [链表实现]
while (iterator.hasNext) { // hasNext 方法可以判断是否有下一个
println(iterator.next()) // next 取出下一个值
}
val iterator2 = List(11, 22, 33, 44, 55).iterator
for(enum <- iterator2) {
println(enum)
}
7.2.9 扩展
-
Stream流
-
stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1) -
view视图
-
view方法产出一个总是被懒执行的集合。 -
view不会缓存数据(结果),每次都要重新计算,比如遍历View时
val viewSquares2 = (1L to 900000l)
.view //view 会懒加载
.filter(eq) -
并行集合
-
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。 -
主要用到的算法有:Divide and conquer : 分治算法,Scala通过splitters(分解器),combiners(组合器)等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回 Work stealin算法(工作窃取算法),主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的 (1 to 10).par.foreach(println)//并行执行
8.模式匹配
8.1 match
-
类似java switch
val oper = '+'
oper match {
case '+' => res = n1 + n2
case '-' => {
res = n1 - n2
}
case '*' => res = n1 * n2
case 11 => println("11")
case '/' => res = n1 / n2
case myOper => println(myOper) //模式中的变量,会先将oper赋值给myOper
case _ => println("oper error")//_相当于default
}
8.2 守卫
-
表达匹配某个范围的数据
case _ if name.equals("woodie") => println("King")
8.3 类型匹配
def main(args: Array[String]): Unit = {
val a = 1
val obj = if(a == 1) 1
else if(a == 2) "2"
else if(a == 3) BigInt(3)
else if(a == 4) Map("aa" -> 1)
else if(a == 5) Map(1 -> "aa")
else if(a == 6) Array(1, 2, 3)
else if(a == 7) Array("aa", 1) //Array[Any]
else if(a == 8) Array("aa")
//1. 下面的方式是进行类型匹配
//2. 这种按照类型匹配的应用场景,通常在网络并发的使用.
//3. 执行流程 case a : Int => a
//3.1 a = result
//3.2 判断 a 的类型是不是 Int, 如果是就匹配成功,否则匹配失败.
val result = obj match {
case _ : BigInt => Int.MaxValue //这里表示忽略匹配的变量值
case a : Int => a
case b : Map[String, Int] => "对象是一个字符串-数字的Map集合"
case c : Map[Int, String] => "对象是一个数字-字符串的Map集合"
case d : Array[String] => {
println(d.mkString(" "))
"对象是一个字符串数组"
}
case e : Array[Int] => "对象是一个数字数组"
case f : BigInt => Int.MaxValue
case _ => "啥也不是"
}
println(result)
}
8.4 匹配数据结构
-
数组
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0),
Array(1, 1, 0), Array(1, 1, 0, 1))) {
val result = arr match {
case Array(0) => "0" //匹配只有0的数组
case Array(x, y) => ArrayBuffer(y,x) //x + "=" + y //匹配有两个元素的数组
case Array(0, _*) => "以0开头和数组"
case _ => "什么集合都不是"
} -
列表
for (list <- Array(List(0), List(1, 0),List(88), List(0, 0, 0), List(1, 0, 0))) {
val result = list match {
case 0 :: Nil => "0" // 匹配的 List(0)
case x :: y :: Nil => x + " " + y // 匹配的是有两个元素的List(x,y)
case 0 :: tail => "0 ..." // 匹配 以0 开头的后面有任意元素的List
case x :: Nil => List(x)
case _ => "something else"
} -
元组
for (pair <- Array((0, 1),(34,89), (1, 0), (1, 1),(1,0,2))) {
val result = pair match { //
case (0, _) => "0 ..." // 表示匹配 0 开头的二元组
case (y, 0) => y //表示匹配 0 结尾的二元组
case (x,y) => (y,x)
case _ => "other" //.默认
}
8.5 对象匹配
-
case中对象的unapply/unapplySeq方法(对象提取器)返回Some集合则为匹配成功 -
返回None集合则为匹配失败
def main(args: Array[String]): Unit = {
val str = "tom,jack,smith"
str match {
case Names(a,b,c) => {
println(s"a=$a b=$b c=$c")
}
case _ => println("匹配失败~~~")
}
}
object Names {
//当对象构建时,有多个参数时,进行对象匹配时,
//会默认调用 unapplySeq方法
//匹配的规则和unapply, 只是 Some(?,?,?)
def unapplySeq(str: String): Option[Seq[String]] = {
if (str.contains(",")) Some(str.split(",")) //Some("tom","jack","smith")
else None
}
}
8.6 变量声明中的模式匹配
val (x, y) = (1, 2)
val (q, r) = BigInt(10) /% 3 //说明 q = BigInt(10) / 3 r = BigInt(10) % 3
val arr = Array(1, 7, 2, 9)
val Array(first, second, _*) = arr // 提出arr的前两个元素
println(first, second) // 1, 7
8.7 for表达式的模式匹配
val map = Map("A"->1, "B"->0, "C"->3) //map集合
for ( (k, v) <- map ) { //每次遍历,取出k和v
println(k + " -> " + v)
}
//只会取出 value= 0 的 k-v
for ((k, 0) <- map) {
println(k + " --> " + 0)
}
//取出 value 在 [0,3] 的范围的 key-val
for ((k, v) <- map if v > 0 && v < 3) {
println(k + " ---> " + v)
}
8.8 样例类case class
object CaseClassDemo {
def main(args: Array[String]): Unit = {
for (amt <- Array(Dollar(1000.0), Currency(1000.0, "RMB"), NoAmount)) {
val result = amt match {
//说明
case Dollar(v) => "$" + v //对象匹配 1000.0
//说明
case Currency(v, u) => v + " " + u // 1000.0 RMB
case NoAmount => ""
}
println(amt + ": " + result)
}
val amt = Currency(29.95, "RMB")
val amt1 = amt.copy() //[默认使用原来值]
val amt2 = amt.copy(value = 19.95) //[使用带名参数, 其它的属性值默认]
val amt3 = amt.copy(unit = "英镑")//..
println(amt1) // Currency(29.95, "RMB")
println(amt2) // Currency(19.95, "RMB")
println(amt3) // Currency(29.95, "英镑")
}
//样例类
//1. 首先它是一个类,只是用case 修饰
//2. 会在底层,默认生成一系列的方法(copy,hashCode,toString,apply,unapply)
// 这些方法,我们不用写,直接使用即可
//3. 样例类会实现Serializable接口
//抽象类
abstract class Amount
//Dollar 样例类, 前面有case关键字
//case class Dollar 说明
//1. 底层会生成两个文件 Dollar.class
//2. Dollar$.class
//3. 默认生成的方法,如果是静态性质的会放在 Dollar$.class
//4. 默认生成的方法,如果是非静态性质的会放在 Dollar.class
case class Dollar(value: Double) extends Amount
//Currency 样例类
case class Currency(value: Double, unit: String) extends Amount
//NoAmount 样例类
case object NoAmount extends Amount
8.9 匹配嵌套结构
object Scala13_match_qiantao {
def main(args: Array[String]): Unit = {
//sale 是一捆书 [单本书 ,Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30) ]
//手工计算: = (40 – 10) + ((80+30)-20) = 120 =>递归计算
val sale = Bundle("书籍", 10, Book("漫画", 40), Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30),Book("漫画", 50)))
//知识1
val res = sale match {
case Bundle(_, _, Book(desc, _), _*) => desc
}
println("res=" + res)
//知识2. 使用@表示法,绑定关心的数据
val res2 = sale match {
case Bundle(_, _, book@Book(desc, price), rest@_*) => (book, rest) //Tuple
}
//取出@绑定的变量的内容
val book = res2._1
println(book)
val rest = res2._2
println(rest)
//知识点3-不使用_*绑定剩余Item到rest
val res3 = sale match {
case Bundle(_, _, book@Book(desc, price), rest) => (book, rest) //Tuple
}
//取出@绑定的变量的内容
val book2 = res3._1
println(book2) // Book
val rest2 = res3._2
println(rest2) //Bundle(文学作品,20.0,WrappedArray(Book(《阳关》,80.0), Book(《围城》,30.0)))
println(price(sale)) // 120.0
}
//编写一个方法,来统计捆商品的总价
//Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30))
def price(item: Item): Double = {
item match {
case Book(_,p) => p
case Bundle(_,discount, its @ _*) => its.map(price).sum - discount //110 - 20 = 90
}
}
}
abstract class Item // 项, 抽象类,价值在于统一管理
//Book样例类,description 描述 , price 价格
case class Book(description: String, price: Double) extends Item
//捆 discount : 打折金额 item: Item* 可以有其它的商品或者Bundle
case class Bundle(description: String, discount: Double, item: Item*) extends Item
8.10 密封类
-
让case类的所有子类都必须在申明该类的相同的源文件中定义,将样例类的通用超类声明为sealed
abstract sealed class Item
9.泛型
9.1 泛型类
-
概念
-
可以接受类型参数的类,在集合类中被广泛使用 -
定义
class Stack[A] {
private var elements: List[A] = Nil
def push(x: A) { elements = x :: elements }
def peek: A = elements.head
def pop(): A = {
val currentTop = peek
elements = elements.tail
currentTop
}
} -
使用
//要使用一个泛型类,将一个具体类型放到方括号中来代替 A。
class Fruit
class Apple extends Fruit
class Banana extends Fruit
val stack = new Stack[Fruit]
val apple = new Apple
val banana = new Banana
stack.push(apple)
stack.push(banana)
//类 Apple 和类 Banana 都继承自类 Fruit,所以我们可以把实例对象 apple 和 banana 压入栈 Fruit 中
9.2 型变
-
协变
-
使用注释 +A,可以使一个泛型类的类型参数 A 成为协变。对于某些类 class List[+A],使 A 成为协变意味着对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 List[A] 就是 List[B] 的子类型 -
逆变
-
通过使用注释 -A,可以使一个泛型类的类型参数 A 成为逆变。与协变类似,这会在类及其类型参数之间创建一个子类型关系,但其作用与协变完全相反。也就是说,对于某个类 class Writer[-A] ,使 A 逆变意味着对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Writer[B] 是 Writer[A] 的子类型。 -
不变
-
默认情况下,Scala中的泛型类是不变的。这意味着它们既不是协变的也不是逆变的
9.3 类型上下界
-
上界
-
类型参数和抽象类型都可以有一个类型边界约束。这种类型边界在限制类型变量实际取值的同时还能展露类型成员的更多信息。比如像T <: A这样声明的类型上界表示类型变量T应该是类型A的子类 -
下界
-
类型下界 将类型声明为另一种类型的超类型。术语 B >: A 表示类型参数 B 或抽象类型 B 是类型 A 的超类型。在大多数情况下,A 将是类的类型参数,而 B 将是方法的类型参数。
差点被微信的排版搞死了,不能完美支持markdown,强烈吐槽
以上是关于scala 知识点总结的主要内容,如果未能解决你的问题,请参考以下文章