// 重点讲述的是与java不同的地方。
1:object:关键字,声明一个单利对象(伴生对象,跟自己的同名类相伴相生),解决静态功能。
// 类型可以推断的时候可以省略,声明的时候就要有初始值。
var 变量名[:变量类型] = 初始值 //可变
val 常量名[:常量类型] = 初始值 //不可变
// Scala中标识符分为两部分
// 1.普通起名和java一样。
// 2.scala中的运算符都可以自己起名
----总结,报错就改。尽量别用$开头
// 3.如何在java中访问scala对象?
---直接对象.方法调用即可。
// 4.非要使用关键字命名的时候,可以加飘号。
// scala中没有字符串,使用的就是java中的字符串,所以可以直接使用java中字符串的方法。
// 补充:什么叫Json?
--javaScript object notition。对象字符串。
--一个{}就代表一个对象,里面就是属性和属性值,如果有多个对象的话,可以用[]括起来所有的{}。
--在网络中传输对象需要序列化,太麻烦了,所以可以把对象包装为Json格式,就可以直接传输了。
---所以把数据转化为Json字符串是一个重要操作。
//字符串输出
---------------用这个--------------
(1)插值字符串:通过$获取变量值
// 前面要加s
println(s"${age}岁的${name}在学校复习")
(2)多行字符串
---主要用于封装Json或者SQL命令
S"""
|select *
|from
| student
|where
| name=${name}
|and
| age > ${age}
|""".stripMargin
// 标准化屏幕输入
val age:Int = scala.io.StdIn.readInt()//阻塞,直到控制台有输入、回车。读一个整数
println(age)
StdIn.readline()//读一行。
// 从文件中获取输入
1.从文件中读取数据,并按原格式打印出来。
//IDea中的取的是相对路径,相对的是当前的project而言。
// 绝对路径:不可改变的路径
--协议://地址(路径),有的没写会默认补全。
//网络:http://www.baidu.com:8/test/test.html
//本地:file://c:/test/test.html
// 相对路径:可以改变的路径,应该存在一个基准路径
--idea中默认的基准路径就是项目的根路径。
Source.fromFile("路径").foreach(print)
2.将数据写入文件
val writer = new PrintWriter(new File("output/test.txt" ))
writer.write("Hello Scala")
writer.close()
// 网络中的IO
//先定义server
object Scala_IO_Server{
def main(args:Array[String]):Unit = {
val server = new ServerSocket(9999)
println("服务器启动成功....等待客户端连接")
val client:Socket = server.accept() // 阻塞
println("服务器已经连接,等待客户端传送的数据")
val in:InputStream = client.getInputStream
val data:Int = in.read()
println("获取客户端发送的数据" + data)
in.close()
client.close()
server.close()
}
}
//再定义client
object Scala_IO_Server{
def main(args:Array[String]):Unit = {
val client = new Socket("localhsot",9999)
println("服务器连接成功")
val out:OutputStream = client.getOutputStream
out.write(1)// 300
out.close()
println("向服务器发送数据成功")
client.close()
}
}
// 测试时,先启动服务器,然后启动客户端。
// 这里传输的是ASC码,不能传输数字。ASC码的范围
// 网络IO-对象输出流
//先定义server
object Scala_IO_Server_Obj{
def main(args:Array[String]):Unit = {
val server = new ServerSocket(9999)
println("服务器启动成功....等待客户端连接")
val client:Socket = server.accept() // 阻塞
println("服务器已经连接,等待接收客户端传送的数据")
val objIn = new ObjectInputStream(client.getInputStream)
val obj = objIn.readObject()
println("获取客户端发送的数据" + data)
in.close()
client.close()
server.close()
}
}
//再定义client
object Scala_IO_Server_Obj{
def main(args:Array[String]):Unit = {
val client = new Socket("localhsot",9999)
println("服务器连接成功")
val user = new User();
user.age = 20
val objOut = new ObjectOutputStream(client.getOutputStream)
objOut.writeObject(user)
objOut.close()
println("向服务器发送数据成功")
client.close()
}
}
//定义一个对象,并且序列化
public class User implements Serializable{
public static int age;
@override
public String toString(){
return "User["+age+"]";
}
}
// 测试时,先启动服务器,然后启动客户端。
1. Scala中一切的数据都是对象,都是Any的子类。
2. Scala中数据类型分为两大类:数值类型 AnyVal,引用类型AnyRef,不管是值类型还是引用类型都是对象。
3. Scala数据类型仍然遵守,低精度的值类型向高精度值类型自动转换(隐式转换)。
4. Scala中的StringOps是对java中的string的增强。
5. Unit 对应java在的void,只有一个对象,就是()
6. Null是一个类,只有一个对象就是null,是所有引用类型的子类。
7. Nothing,是所有数据类型的子类,主要用在一个函数没有明确返回值的时候使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。(比如一个函数在运行的时候,抛出了异常,根本就不考虑他的返回值。)
val n2:Int = (2.9 + 3.7).toInt
--强转会造成精度损失,因为是截断。
程序中输入的数字到底层的时候会以补码的形式存储,正数的补码就是自己,负数的补码就是自己全部取反再+1。在补码中,整数的符号位是0,负数的符号位是1,强转就是在补码上直接截断。
数值类型和String类型的转换。
//(1)数值转换String
val n:Int = 27
val s:String = n +""
//(2)String转数值
val m:Int = "12".toInt
val f2:Int = "12.3".toDouble.toInt运算符
逻辑运算符
--在java中==判断地址,object.equals()判断值。
--在scala中,==和.equals()被统一为判断值。用.eq()判断引用地址。
在scala中,==底层调用了.equals()。
赋值运算符
在scala中是没有 ++ --的,避免产生歧义。
--在java中
x=23
x=x++会被分解为 tmp = x++ ,x = tmp
tmp=23,x++后x变为24,但是x=24后又被tmp=23覆盖为23
// Scala运算符是一种方法,只是省略成为了计算形式。
// 字符串也可以有乘法。
// n + m 等价 n.+(m)
-----这也解释了为什么 +等可以作为标识符。
在scala中调用方法可以用空格代替.
n +(m)
进一步的,()里面只有一个m,可以把()去掉变为n + m
java:7.5.toString()
scala:7.5 toString
连续调用的时候更明显
scala:7.5 toInt toString
// Scala中的表达式都是有返回值的,每一个分支都有返回值。
-----就是该分支最后一行表达式的值。
for循环
// 1.增强for循环,常使用,先准备一个集合、
//for循环里面的数据集可以是任意类型的数据集合,比如字符串(视为字符数组)、集合、数组等。
val list = 1 to 5 // 1.to(5)
val list = 1 until 5 // 1,2,3,4
val list = range(1,5) // 1,2,3,4,默认步长为1,第三个参数可以设置步长。
val list = range(1,5,2)
for(num <- list){
println(num)
}
val s = "zhangsan"
for(c <- s){
println(c)
}
// 循环守卫(增加的判断条件)
for(i <- Range(1,5) if i != 3){
println(i)
}
// 循环步长
for(i <- 1 to 5 by 2){
println(i)
}
// 嵌套循环
//每次i和j同时进入一次。
for(i <- Range(1,5);j <- Range(1,4)){
println("i = " + i + ",j = " + j)
}
-------------这里的代码块会在两个循环中都使用到
for(){
// 如果在这个位置写代码,就要用这种循环。
for(){
}
}
// 循环的返回值
//循环的默认返回值为Unit,但可以利用yield关键字可以返回每一步的执行结果。
val result = for(i <- 1 to 4)yield{
i*2
}
println(result) // vector(2,4,6,8)
// 循环跳出
breakable{
for(i <- 1 to 5){
if(i == 3){
break
}
println(i)
}
}
如何区分函数和方法?
1.简单理解:方法也是函数。函数声明在类中称方法,其他场合是函数。方法有重写、重载,函数没有这些。但函数可嵌套声明使用,方法不行。
2.scala 方法是类的一部分,而函数是一个对象,可以赋值给一个变量。
3.Scala中函数会编译为方法,且改了方法名字,方法名字后增加$1,嵌套的同名方法会变加上$2……,而且增加了private final static。调用时会自动更改为变了名字和修饰符的方法。
4.Scala语言不关心函数的实现方式,只关心如何调用。
对一个Scala函数来讲,最重要的是IO,即需要传入什么,需要返回什么。
1.无参无返回值
// 声明,右侧是一个代码块,代码块是有返回值的,相当于赋值操作。
def fun1():Unit = {
}
// 调用
fun1()
--无参的时候可以省略(),变为fun1,所以单独出现一个函数名fun1的时候,就是认为在调用和执行该函数。如果fun1_则考虑为一个静态对象,如果是fun1()则考虑为调用或者静态对象。
2.无参有返回值
// 声明
def fun2():String = {
return "zhangsan"
}
// 调用
val str:String = fun2()
--无参的时候可以省略(),变为val str:String = fun2
println(str)
3.有参无返回值
// 声明
def fun3(s:String):Unit = {
println(s)
}
// 调用,必须传参。
fun3("zhangsan")
--可以传递子类对象类型。
4.有参有返回值
// 声明
def fun4(name:String):Unit = {
return "Name:" + name
}
//调用
val name:String = fun4("zhangsan")
println(name)
5.多参无返回值
// 声明
def fun5(name:String,age:Int):Unit = {
println(s"Name:${name},Age:${Age}")
}
// 调用
fun5("zhangsan",10)
--传参必须按顺序按类型。
6.多参有返回值
// 声明
def fun6(name:String,age:Int):String = {
// 这里将属性封装到json中。
val json =
s"""
|{"name":"${name}","age":${age}}// json里面的属性名和属性值都需要用双引号包起来,但是如果属性名不是字符串类型时,可以省略双引号。
|""".stripMargin
return json
}
// 调用
val json = fun6("zhangsan",10)
println(json)
7.可变参数
// 声明
def fun7(age:Int,name:String*):Unit={
println(name)
}
--如果参数不确定,且参数之间含义相同,可以使用可变参数。
--参数类型后面使用*,表示可变参数,调用时可以传递参数,也可以不传。
--可变参数要放到参数列表的最后面。(所以不可能有两个可变参数。)
// 调用
fun7()
--不传参数的时候,不可以省略小括号,此时打印获取的结果是一个空集合List()。
fun7("zhangsan")
--传值后,打印输出的结果会被包装为一个集合WrapperArray(zhangsan)
8.带默认值的参数
// 声明
def fun8(password:String = "000000"):Unit = {
println(password)
}
--默认值放在类型的后面。
--带默认值的参数不允许和可变参数一起使用。
// 调用
fun8()
--不传参的时候,会使用默认值
fun8("123123")
9.带名传参
// 声明
def fun9(password:String = "000000",name:String) = {}
// 调用
fun9(name="zhangsan")
---遇到这种比较棘手的情况的时候,可以使用 形参名=值的方式传参调用。
// 能省则省
1. return 可以省略
def test():String = {
return "zhangsan"
}
2.函数体只有一行,可以省略{}
def test():String = "zhangsan"
3.如果根据返回结果可以推断返回值类型,那么返回值类型可以省略。
---如果出现选择结构,可能有多个分支返回不同的类型,那么返回值类型会向高级自动推断。
def test()="zhangsan"
4.如果该函数没有参数,参数列表小()可以省略。
def test = "zhangsan"
----如果声明的时候没加小括号,那么使用的时候也不能加小括号。
5.如果函数明确声明返回值类型为Unit,那么函数体中仍然可以声明return,但是无法起作用,主要目的是为了改变分支。
如果函数体中使用return返回结果(虽然不起作用),那么返回值类型必须声明。
def test():Unit = {
return "zhangsan"
}
如果我们希望省略Unit,也要让return失效,那么可以省略Unit和=
def test(){
return "zhangsan"
}
6.def和函数名也可以省:匿名函数,但需要转换格式。
(s:String) => {
println("zhangsan")
}
1.将函数对象赋值给一个变量,那么这个变量的类型就是函数类型in => out, x∈【0,22】
// 这里声明一个函数,等于声明一个函数对象。即对象。
def fun():Unit = {return "zhangsan"}
// 将函数对象赋值给一个变量。
val ff: () => Unit = fun // 也可以 val ff = fun_
// 怎么使用这个变量呢?一旦加上(),就表示执行了该函数,即加括号表示调用该函数,不加括号表示函数对象。
ff()---调用函数
ff ----函数对象
---引入匿名函数:
// 直接在等号右侧给一个匿名参数,避免先声明再赋值。
val ff:() => Unit = ()=>{return "zhangsan"}
1.1 将函数返回值赋给一个变量。
def fun():Unit = {}
而调用fun()时,因为没有参数,可以直接使用fun
// 这里判定为将函数返回值赋给一个变量
val f = fun()
// 如果仍然只是想用函数对象,而不是返回值,则在函数名后加上下划线。
val f = fun_
// 或者
val f:()=>Unit = fun
1.2 函数类型的in => out中,如果in的参数只有一个,小括号可以省略。
2.将函数对象作为参数传递给另一个函数。
def fun1():Unit = {}
def fun2(fun1:() => Unit):Unit = {f()}
fun2(fun1)
def sum(a:Int,b:Int):Int = {a + b}
def fun(f:(Int,Int) => Int):Int = {
val r = f(10,20)
println(r)
}
fun(sum)
---将传入参数转变为匿名参数,以匿名参数作为参数传入。
fun(
(a:Int,b:Int) => {
a - b
}
)
---引入匿名参数的至简原则,把匿名函数也能省则省。
fun((a:Int,b:Int) =>{a - b})
fun(a:Int,b:Int) =>a - b)
---只有一行,可以省略{}
fun(a,b) =>a - b)
---参数类型在传参后可以推断出来,可以省略参数类型
fun(_-_)
---参数按顺序只访问了一次,那么参数列表可以省略,表达式中的参数可以用下划线代替。
例:
def fun(s:String):Unit = {
println(s)
}
def test(f:String => Unit):Unit ={
f()
}
test(fun)
test(
(s:String)=> {println(s)}
)
---test((s:String)=>println(s))
---test((s)=>println(s))
---test(println(_))
还可以继续,但这里已经不是函数至简原则了,相当于回到了最开始,在一个函数中传入另一个函数对象。
---test(println)
3.将函数作为返回值使用
def test():Unit = {}
def fun():Unit = {
test //这里是执行了test,所以返回Unit。
}
def fun1() = {
test_ //这里没有执行test,而是将test作为一个函数对象返回。返回值类型就是test函数的类型。
}
val f = fun()
// 在实际使用中:
def outer():Unit={
def inner():Unit={
println("inner....")
}
inner_
}
// 调用
outer(外层参数)(内层参数)
4.闭包
// 在上面,内层函数是没有使用到外层函数的参数的,那如果内层函数运用到外层函数的参数呢,函数就是方法,外层方法结束后会弹栈,也会带走自己这一层的参数。所以需要闭包。
// 闭包的意思就是将外层的参数打包保存到heap中下来,防止外层函数执行结束后弹栈,带走了参数。相当于延长了外层参数的生命周期。
// 闭包的应用场景:大数据场景下,固定相同的数,然后改变其他加数。
// 将固定的加数作为另一个参数传入,但是是作为”第一层参数传入“
5.控制抽象
// 如果函数声明时,有一个参数是函数对象,且这个函数对象的参数列表没有写,就是抽象。
// 调用的时候不可以增加参数列表的小括号。只需要传递逻辑代码,传递多行逻辑代码的时候()要变为{}
// 应用场景:模板设计模式
def test(f: =>Unit):Unit={
f //使用该不完整参数时,也要保持没有参数列表。
}
// 调用test时,只需要传递逻辑代码。
test(println("zzzzz"))
object Function_Desigh{
def main(args:Array[String]):Unit={
// 声明规范
def operationDB(op:=>Unit):Unit={
println("init connection...")
op
println("close connection....")
}
// 使用时传入逻辑。
operationDB{
println("operation db......")
}
}
}
6.函数科里化
// 将无关的参数给分离开,形成多个参数列表
def test(i:Int)(j:Int):Unit={
for(a <- 1 to i){
println(a)
}
for(a <- 1 to j){
println(a)
}
}
// 使用,有的参数列表可以根据场景省略。
test(i)(j)
7.递归
// 下面是正常的递归。
// 方法自己调用自己,需要有跳出逻辑(但仍可能栈溢出,递归此处太多。)
--递归在IDEA中有一个回环图标就表示递归。
// 递归函数中不可以省略返回值类型
def test(num:Int):Int={
if(num <= 1){
1
}else{
num * test(num - 1)
}
}
----引入尾递归,尾递归在scala中执行完方法体后,会立即弹栈,然后去递归执行下一个函数,因为一直弹栈,所以不会栈溢出。
def test():Unit={
println("abc")
test()
}
8.惰性函数
// lazy修饰,可以使函数的执行会被推迟,知道我们首次用上这个函数的返回值的时候,该函数才会执行。
def fun():String={}
lazy val f = fun() // 暂时不执行,等到打印f时才执行。
println("zhangsan")
println(f)
// 定义Master对象,所有的slaver节点都需要向master注册,让master知道集群里有多少个节点。另外Master可以把自己的元数据交给Zookeeper,可以做成高可用。
object Scala_Function_Master{
def main(args:Array[String]):Unit = {
// 准备数据
val num = 4
// 准备逻辑
val logic = (num:Int) => {println(num)}
// 调度(按特定方式,切分任务)
val task1 = new Task()
task1.start = 1
task1.end = num/2
task1.logic = logic
val task2 = new Task()
task2.start = num/2 + 1
task2.end = num + 1
task2.logic = logic
// 给节点发送切分完成的任务
val slaver1 = new Socket("localhost",9999)
val objOut1 = new ObjectOutputStream(slaver1.getOutputStream)
objOut1.writeObject(task1)
objOut1.flush()// flush的作用是清空缓存区的数据,使缓存区的数据写出去,避免close时有数据丢失
objOut1.close()
val slaver2 = new Socket("localhost",8888)
val objOut2 = new ObjectOutputStream(slaver2.getOutputStream)
objOut2.writeObject(task2)
objOut2.flush()// flush的作用是清空缓存区的数据,使缓存区的数据写出去,避免close时有数据丢失
objOut2.close()
//
slaver1.close()
slaver2.close()
}
}
// 定义一个类封装计算任务,任务对象要从master发往slaver,涉及到传输对象,所以需要序列化。
class Task extends Serializable{
val start:Int = 0
val end:Int = 0
val logic :(Int) => Unit = null
def run():Unit = {
for(i <- start to end){
logic(i)
}
}
}
// 定义slaver1,slaver可以动态扩容,每个节点的逻辑都是一样的,所以只需要外面动态的传递端口号就行了。
object Scala_Function_Slaver1{
def main(args:Array[String]):Unit = {
val server = new ServerSocket(9999)
println("slaver[9999]服务启动,等待master连接")
val master:Socket = server.accept()
println("slaver[9999]等待master传输数据")
val objIn = new ObjectInputStream(master.getInputStream)
val obj = objIn.readObject()
val task:Task = obj.asInstanceOf[Task]
// 执行任务
task.run()
objIn.close()
// 一般是不需要停止的,这里是演示而已。
master.close()
server.close()
}
}
// 定义slaver2
object Scala_Function_Slaver1{
def main(args:Array[String]):Unit = {
val server = new ServerSocket(8888)
println("slaver[8888]服务启动,等待master连接")
val master:Socket = server.accept()
println("slaver[8888]等待master传输数据")
val objIn = new ObjectInputStream(master.getInputStream)
val obj = objIn.readObject()
val task:Task = obj.asInstanceOf[Task]
// 执行任务
task.run()
objIn.close()
// 一般是不需要停止的,这里是演示而已。
master.close()
server.close()
}
}
测试的时候,需要先启动slaver1和slaver2,然后启动master。
//1.可以声明变量,但是不能使用
val _ = "zhangsan"
//2.将函数作为对象来使用。
val f = test_
//3.在匿名函数中可以代替参数,但是不可以嵌套使用。另外如果参数给定什么,就返回什么的场合,是不可以使用下划线代替的,如word => word是不可以的。
test(_+_)
//4.导入类时,可以代替*
import java.util._
//5.属性的默认初始化
var name:String = _
//6.模式匹配中,作为任意值进行匹配,类似于java中国switch中的default操作。
case_ => xxxxx
//7.泛型中,下划线表示任意类型。
// 1.同一个源码文件中子包(内包)可以直接访问父包(外包),而无需import
// 2.外包访问内包,需要导包。
// 3.package也可以看作对象,并声明属性和函数,作为其对应包下的所有class和object的共享变量,可以直接被访问。
// 1.import关键字可以声明在任何要用的地方
// 2.同一个包中的类可以简化为一行,用一个花括号括起来,用逗号隔开。
import java.util.{ArrayList,HashMap}
// 3.导包。包是个对象,那么包里面的东西都可以使用了。
import java.util
new util.ArrayList()
// 4.屏蔽类
import java.util._
import java.sql.{Data =>_,_}
// 5.改类名
import java.util.{HashMap=>JavaHashMap}
// class声明类, new构建对象。
// scala中所谓类的属性其实就是类中声明的变量
//但是作用域不一样
class User{
val name = "zhangsan"
var age = 30
// var email : String是错的,因为scala中变量必须使用显示初始化。可以用下划线代替,但是val不可以这么使用,因为val后续不许更改,占位没有意义。
var email : String = _
}
// scala给类声明属性,在编译后,这个属性是私有的,但是同时编译出了两个公共方法(不是java中的那种setXXX和getXXX方法),用于set/get属性。
// java中很多的框架要求bean对象必须符合bean规范(属性私有化,并提供标准get/set方法)。scala做出的处理是在var属性前面加上一个注解:@BeanProperty,这样属性就具有了set/get方法。
// scala权限
1.private 同类
2.private[包名] 同类、同包
3.protected 同包 同类 子类
4.(public,默认)公共
// 方法就是函数,只不过是声明在类中。
//对象.eq()方法可以直接使用,比较内存地址
//对象.isInstanceOf()方法可以直接使用,表示是否为每个类型的实例。
//对象.asInstanceOf()可以直接使用,表示将对象作为某个类型的实例来使用。
// 万物皆函数,类其实就是函数。scala中的构造方法分为两大类
//1.完成类的初始化:主构造方法
//2.辅助操作:辅助构造方法,用this关键字声明的构造方法
---辅助构造方法必须要首先完成类的初始化操作,然后再进行辅助操作,所以必须在代码中首先调用主构造方法。
---辅助构造方法之间可以相互调用,调用前必须声明过。
class User{ // 主构造函数
var username : String = _
def this( name:String ) { // 辅助构造函数,使用this关键字声明
this() // 辅助构造函数应该直接或间接调用主构造函数
username = name
}
def this( name:String, password:String ) {
this(name) // 构造器调用其他另外的构造器,要求被调用构造器必须提前声明
}
}
// 与java一致
// 如果一个类中有抽象方法,那么这个类需要用abstract修饰为抽象类。
// 抽象方法直接写出声明,不用方法体。
// 抽象类中完整的方法也可以被实现类重写,但是要在方法前加上关键字override,跟java的@Override注解不一样。
// 声明属性而没有初始化,称之为抽象属性。重写抽象属性补充完整即可。重写完整val属性,需要在属性前面增加override关键字。
abstract class User{
def test():Unit
}
// Scala如果想使用到单例模式,直接在类的前面使用object关键字就行。
object User{
}
//若单例对象名与某个类的类名相同,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明,然后通过伴生对象名称直接调用
// 一般会将成员方法和属性声明在伴生类中。
// 一般会将模拟静态方法和静态属性声明在伴生对象中。伴生对象可以访问半生类中的所有内容。
// scala中为了方便构建对象,如果构建对象的方法名是一个特殊的名称的话,那么这个方法名可以不用写。
// 伴生类
class User{
}
// 伴生对象
object User{
// apply可以给不同参数,重载。
def apply():User = {
new User()
}
}
// 构建对象
val user2 : User = User.apply()
// apply 可以省略,集合中常用
val user1 : User = User()
// 通过调用伴生类的构造方法来new。如果伴生类声明的时候加上了private,这种方法就没法使用了。
val user3 : User = new User()
// 单例对象,即本省就是一个对象(User$)
val user4 : User = User
// 将多个对象中相同的特征剥离出来,形成一个独立的语法结构,称之为特质(特征)
// 如果有一个类符合这个特征,那么可以将这个特征混入到这个类中。
// 如果一个类可以混入多个特征,第一个特征使用extends,后续使用with连接。
// 如果一个类既有父类,同时还混入了特征,那么父类使用extends关键字,所有特征都使用with连接。
---scala中用trait统一了java抽象类和接口。java中的所有接口在scala中都是特质。
trait Runable{
def run()
}
class Person extends Runable{
override def run():Unit = {
println("person runnning..........")
}
}
// 特质的动态混入:在new对象的时候混入新功能,可以避免修改源码。
val user = new User() with Runable
// 特质初始化顺序
1.如果类和特质同一个级别,那么特质先初始化
2.如果多个特质混入,那么初始化顺序是从左到右
3.初始化只会执行一次。
// 特质功能叠加
从右到左。super【父类名】可以指定跳跃到某个父类特质。
// 不可变 scala.collection.immutable 方法尽量使用运算符,默认情况下,scala中的集合都是不可变的。
// 可变 scala.collection.immutable 方法使用英文单词
//分可变集合和不可变集合的意义
// 在数仓中,我们会将原始数据保留住,对原始数据进行一些数据清洗的操作,然后把清洗的结果发送的另一个地方去。如果下次我们仍然想使用原始数据,就应该将原始数据保存在一个不可变集合中。(重点是强调不可变,因为java中是可变的。)
// 数组的声明方式2,声明的同时赋值。----这是最常见的方式。
val arr:Array[Int] = Array(1,2,3)
1. 访问、赋值、修改元素(通过索引)
arr(0)=3
2. 遍历
// 遍历方式1
arr.mkString(",")
// 遍历方式2
arr.foreach(println)
// Scala中的集合默认是不可变的。如果运算符使用冒号结尾,那么运算规则从右向左。
// 增加元素+。(冒号的位置就是原集合的位置)
val ints:Array[Int] = arr :+ 4 //(1,2,3,4)
val ints1:Array[Int] = 5 +: arr //(5,1,2,3)
// 把另一个集合的元素链接过来++,记一个就行,自己更改朝向。
// array={4,5,6},array1={1,2,3}
val ints2:Array[Int] = arr ++: arr1,//{4,5,6,1,2,3}
val ints3:Array[Int] = arr1 :++ arr //{1,2,3,4,5,6}
// 多维数组(数组里面有数组) 3*3二维数组。
var myMatrix = Array.ofDim[Int](3,3)
// 二维数组遍历,这里的list只是一个变量名,完全可以使用arr。
myMatrix.foreach(List=>println(List.mkString(",")))
// 合并数组
val arr1 = Array(1,2,3)
val arr2 = Array(4,5,6)
val arr3:Array[Int] = Array.concat(arr1,arr2)
// 创建指定范围的数组
val arr7:Array[Int] = Array.range(0,2) // [0.2)
// 创建并以指定元素填充数组,下面是以-1填充长度为5的数组。常用于状态监测。
val arr8:Array[Int] = Array.fill[Int](5)(-1)
// new 一个空的
val buff = new ArrayBuffer[String]()
// 赋值构建
val buff1 = ArrayBuffer("1","2","3")
// 追加元素,后面是可变参数
buff.append("a","b","c")
// 追加集合
buff.appendAll(buff1)
// 遍历方式1,可变的数组直接打印。
println(buff)
// 更改元素,更改自身
buff.update(index,element)<=> buff(index) = element
// 更改元素生成新的集合,不改变原来的集合
val ints:ArrayBuffer[Int] = buff.updated(index,element)
// 插入元素,在index处,插入指定数量的元素(可变参数)
buff.insert(index,ele1,ele2,........)
// 删除元素
buff.remove(index)
// 从index处,删除count个元素,注意不要越界。
buff.remove(index,count)
val array = Array(1,2,3,4)
val buffer = ArrayBuffer(1,2,3,4)
// 不可变转换 => 可变
array.toBuffer
// 可变 => 不可变
buffer.toArray
// 不可变的Seq特质,使用List最多。
val list = List(1,2,3,4,1,4)
val list1 = List(1,3,2,4)
// 思考,为什么Java中要这么写?
List list = new ArrayList()
// 而不是这么写
ArrayList list = new ArrayList()
// 当考虑拓展性后,后面无论是哪个类实现了List接口,只要左边是List接口,list.add()都是可以使用的。
// 当使用ArrayList特有的方法,用Arraylist类。
// 增加数据::
val 1ist2 = 1::2::3::list::List(1) //把1,2,3和list集合都加入到List中。
val list3 = 1::2::3::Nil // 增加数据到空集合
val list4 = 1::2::3::list:::Nil // 将整体的list拆分为个体,放入到新集合中(叫做扁平化)
val buffer = ListBuffer(1,2,3,4)
buffer.append()
buffer.appendAll()
buffer.insert()
buffer.update()
buffer.updated()
buffer.remove()
val list = List(1,2,3,4)
val buffer = ListBuffer(1,2,3,4)
// 不可变转换 => 可变
list.toBuffer
// 可变 => 不可变
buffer.toList
// 因为set是无序的,所以就没有了索引操作。
val Set = Set(1,2,3,4)
// 创建
val set = mutable.Set(1,2,3,4)
//增加数据,没有索引、顺序,所以是单纯的增加。
set.add(5)
//修改集合,true就是使集合包含该元素,fasle就是使集合不包含该元素。
set.update(element,false)
// 删除元素
set.remove(2)
// 遍历
println(set.mkString(","))
set.foreach(println)
// 键值对
val kv = "a" -> 1
// 无序,kv键值对,k不可重复,v可以重复。a指向3,后者将前者覆盖了。
val map = Map("a"->1,"b"->2,"c"->3,"d"->3,"a"->3)
// 创建
val map = mutable.Map("a"->1,"b"->2,"c"->3,"d"->3,"a"->3)
val map = mutable.Map(("a",1),("b",2),("c",3))
// 删除
map.remove("c")
// 遍历
map.mkString(",")
map.foreach(println)
// 如果元组中的元素只有两个,称之为对偶元组,也称之为键值对对象
"a"->1,等价于 ("a",1)
// map遍历
map.foreach(
kv =>{
println(kv._1 + "=" + kv._2)
}
// 增加/修改
map.update("a",5)
map("a") = 5
map.put("a",4)//返回值类型为Option
// 如果有该key,就返回value,如果没有改key,就返回默认值。
map.getOrElse("a",0)
// 如果有该key,就返回value,否则将该key和value增加进map。
map.getOrElseUpdate("a",1)
// 单独取出所有的key
val keys:Iterable[String] = map.keys
// hashmap是可以放空key,空null的
map.put(null,null)
//hashtable是不可以放空key,空null的。
// 排序,因为map没有索引的概念,所以没有sort方法,要先转化为List
val list1 = map.toList
list1.sort()
----当List中有kv时,也可以转换为Map。
// scala将无关的数据作为整体使用的时候,形成一个元素的组合,简称为元素,称之为元组Tuple
//声明
val tuple = (1,"zhangsan",30)
//因为数据无关,所以一般通过位置编号进行访问。
println(tuple._1)
// 遍历
tuple.productElement(index)
tuple.Iterator
// 迭代(套娃)
t->2->3->4->5
//Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
object ScalaCollection{
def main(args: Array[String]): Unit = {
val que = new mutable.Queue[String]()
// 添加元素
que.enqueue("a", "b", "c")
val que1: mutable.Queue[String] = que += "d"
println(que eq que1)
// 获取元素
println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
}
}
// Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
object ScalaCollection{
def main(args: Array[String]): Unit = {
val result1 = (0 to 100).map{x => Thread.currentThread.getName}
//par就是并行的意思。
val result2 = (0 to 100).par.map{x => Thread.currentThread.getName}
println(result1)
println(result2)
}
}
object ScalaCollection{
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
// 集合长度
println("size =>" + list.size)
println("length =>" + list.length)
// 判断集合是否为空
println("isEmpty =>" + list.isEmpty)
// 集合迭代器
println("iterator =>" + list.iterator)
// 循环遍历集合
list.foreach(println)
// 将集合转换为字符串
println("mkString =>" + list.mkString(","))
// 判断集合中是否包含某个元素
println("contains =>" + list.contains(2))
// 取集合的前几个元素
println("take =>" + list.take(2))
// 顺序取集合的后几个元素
println("takeRight =>" + list.takeRight(2))
// 查找元素
println("find =>" + list.find(x => x % 2== 0))
// 丢弃前几个元素
println("drop =>" + list.drop(2))
// 丢弃后几个元素
println("dropRight =>" + list.dropRight(2))
// 反转集合
println("reverse =>" + list.reverse)
// 去重
println("distinct =>" + list.distinct)
}
}
2) 衍生集合
object ScalaCollection{
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
val list1 = List(1,2,3,4)
val list2 = List(3,4,5,6)
// 集合头
println("head => " + list.head)
// 集合尾
println("tail => " + list.tail)
// 集合尾迭代
println("tails => " + list.tails)
// 集合初始值
println("init => " + list.init)
// 集合初始值迭代
println("inits => " + list.inits)
// 集合最后元素
println("last => " + list.last)
// 双集合操作
// 集合并集
println("union => " + list.union(list1))
// 集合交集(如果是set,会去重)
println("intersect => " + list.intersect(list1))
// 集合差集,哪个集合调用方法,就以哪个集合为中心。
println("diff => " + list.diff(list1))
// 切分集合
println("splitAt => " + list.splitAt(2))
// 按条件将集合切分为两个符合条件的集合。
list.partition(_>3)
// 滑动窗口,每次滑动一步(窗口大小)
println("sliding => " + list.sliding(2))
// 多步长滑动,(窗口大小,滑动步长)
println("sliding => " + list.sliding(2,2))
// 拉链,将相同位置上的元素连接再一次,谁调用谁在左边,拉不上的就舍弃。
println("zip => " + list.zip(list1))
// 数据索引拉链,将数据和索引构建在一起。
println("zipWithIndex => " + list.zipWithIndex)
}
}
3) 计算函数
object ScalaCollection{
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
val list1 = List(3,4,5,6)
// 集合最小值
println("min => " + list.min)
// 集合最大值
println("max => " + list.max)
// 集合求和
println("sum => " + list.sum)
// 集合乘积
println("product => " + list.product)
// 聚合(两两计算)
println("reduce => " + list.reduce(_+_))
// 聚合(左),从左边开始迭代加括号
println("reduceLeft => " + list.reduceLeft(_+_))
// 聚合(右),从右边开始迭代加括号
println("reduceRight => " + list.reduceRight(_+_))
// 折叠,fold方法存在函数柯里化,第一个参数列表表示集合之外的计算初始值,第二个参数列表表示计算规则:两两计算。要求计算初始值和集合原始值类型相同
println("fold => " + list.fold(0)(_+_))
// 折叠(左),内外类型可以不一样,以外为主。从左开始,迭代加括号。
println("foldLeft => " + list.foldLeft(0)(_+_))
// 折叠(右),先reverse.foldleft,然后把外面元素放在前面,然后左右交换位置上括号。
println("foldRight => " + list.foldRight(0)(_+_))
// 扫描,将每一步的计算结果保留下来。
println("scan => " + list.scan(0)(_+_))
// 扫描(左)
println("scanLeft => " + list.scanLeft(0)(_+_))
// 扫描(右),结果从右向左保存为一个List
println("scanRight => " + list.scanRight(0)(_+_))
}
}
4) 功能函数
object ScalaCollection{
def main(args: Array[String]): Unit = {
val list = List(1,2,3,4)
// 映射map,将每一条数据转换映射,产生新的集合,能改变结构
println("map => " + list.map(_*2))
// 扁平化,整体拆成个体
val list1 = List(
List(1,2),
List(3,4)
)
println("flatten =>" + list1.flatten)
// 自定义扁平化
println("flatMap =>" + list1.flatMap(list=>list))
val list = List(
"hello Scala","hello Spark"
)
val list2 = list.flatMap(
s => s.split(" ") // 这里的s指的是,list中的每一个值。
)
// 过滤,根据指定条件对集合每一个元素进行筛选
println("filter =>" + list.filter(_%2 == 0))
val list1 = List("Hadoop","Hive","Scala","Spark")
list1.filter(
s => {
s.starsWith("S")//每一个值都判断一下是否以S开头。
}
)
// 分组,根据指定规则进行分组,,返回结果为Map类型,其中的key就是分组的表示,value就是相同标识的数据集合。
println("groupBy =>" + list.groupBy(_%2))
// 排序,默认为升序(如果是字符串排序的话,按字典序。)
//单关键字排序
println("sortBy =>" + list.sortBy(num=>num)(Ordering.Int.reverse)
val list1 = List("1","2","3","11","22")
val sortList1 = list1.sortBy(s => s)// 按字典序
val sortList2 = list1.sortBy(s => s.toInt)// 按数值大小
// 多关键字默认升序
//使用元组排序,元组在排序时,会先按照第一个元素排,然后按第二个元素排....()
println("sortWith =>" + list.sortWith((left, right) => {left < right}))
// 自定义多关键字排序,将自己真实想要的排序结果返回为true
val sortList1 = list.sortWith(
(a,b) => {
//将预期结果返回为true
if(a.age < b.age){
true
}else if(a.age == b.age){
a.salary > b.salary
}else{
false
}
}
)
// 改变value
val list = List(
("hello",2),("hello",4),("hello",6)
)
val mapList = list.toMap.mapValues(_*2)
println(mapList)
}
}
object SelfScalaTest {
def main(args: Array[String]): Unit = {
//1.获取文件原始数据
val source: BufferedSource = Source.fromFile("data/word")
//返回每行的迭代器
val strings: Iterator[String] = source.getLines()
//可以将全部迭代结果(每一行的所有元素),返回到一个list中
val list: List[String] = strings.toList
source.close()
//2.将原始数据分解为一个一个的单词
val strings1: List[String] = list.flatMap(_.split(" "))
//3.将单词分组
val stringToStrings: Map[String, List[String]] = strings1.groupBy(s => s)
//4.对分组后的单词进行统计
val stringToInt: Map[String, Int] = stringToStrings.map(
kv => (kv._1, kv._2.size)
)
//5.对分组统计完成之后的touple排序降序
val tuples: List[(String, Int)] = stringToInt.toList.sortBy(
t => t._2
)(Ordering.Int.reverse)
//6.将排序结果输出到打印台
println(tuples)
}
}
// 初学的时候最好把类型带出来.
object ScalaWordCount{
def main(args: Array[String]): Unit = {
// 将文件中的数据读取出来,放到list中
val lineList: List[String] = Source.fromFile("input/word.txt").getLines().toList
source.close()
val top3 =
LineList
.flatMap(_.split(" "))
.groupBy(word=>word)//不可以简化,简化会报错。
.mapValues(_.size)//mapValues返回的是一个map
.toList
.sortBy(t => t._2)(Ordering.Int.reverse)
.take(3)
println(top3)
}
}
// 工作中要先写出来。
object Scala_Collection_Test{
def main(args:Array[String]):Unit={
val list = List(
("hello",4),
("hello spark",3),
("hello spark scala",2),
("hello spark scala hive",3)
)
// 将上面的数据进行WordCount后排序取前三名
//方法1
val mapList = list.map(
t => (t._1 + " ") * t._2
)
val top3 =
LineList
.flatMap(_.split(" "))
.groupBy(word=>word)
.mapValues(_.size)
.toList
.sortBy(t => t._2)(Ordering.Int.reverse)
.take(3)
// 方法2
//拆分为局部WordCount
val flatList = list.flatMap{
t => {
val str = t._1
val cnt = t._2
val words = str.split(" ")
word.map((_,cnt))
}
}
//分组
val groupMap:Map[String,List[(String,Int)] = flatList.groupBy(_._1)
//局部统计
val sumMap = groupMap.mapValues(list.list.map(_._2).sum)
//排序
val top = sumMap.toList.sortBy(t => t._2)(Ordering.Int.reverse)
}
}
// Scala中的模式匹配类似于Java中的switch语法
// 顺序匹配分支,成功则执行分支代码,不成功,继续下一个分支判断。case _分支兜底(一般放到最后,在前面就顺序执行)。无法匹配成功会报错
var a: Int = 10
var b: Int = 20
var operator: Char = 'd'
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result)
// 1.匹配常量
// 2.匹配类型
---匹配类型时,类型前要加临时变量名,后续的计算要使用该变量名参与计算。
---忽略泛型(你写不写都没用)。
---数组需要考虑泛型(转化为字节码的时候不是泛型,泛型就是数组的元素类型)。
// 3.匹配数组
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对一个数组集合进行遍历
val result = arr match {
case Array(0) => "0" //匹配Array(0) 这个数组
case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组
case _ => "something else"
}
println("result = " + result)
}
// 4.匹配列表
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {
val result = list match {
case List(0) => "0" //匹配List(0)
case List(x, y) => x + "," + y //匹配有两个元素的List
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
// 看集合能否分为三个部分。 ::是连接,空集合也是一个部分。
case first :: second :: rest => println(first + "-" + second + "-" + rest)
case _ => println("something else")
}
// 5.匹配元组
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
case (0, _) => "0 ..." //是第一个元素是0的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
---------------------------使用场景---------------------
///过滤
val (id,name,age,_) = (1,"zhangsan",30,100)
// 6.样例类(比较类的内容)
case class User(name: String, var age: Int)
object ScalaCaseClass {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
// 7.匹配偏函数,只对集合中的部分数据进行处理。
//将该List(1,2,3,4,5,6,"test")中的Int类型的元素加一,并去掉字符串。
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)