Scala基础
Posted aidata
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala基础相关的知识,希望对你有一定的参考价值。
OOP 面向对象编程 AOP 面向切面编程 FP 函数式编程 编程语言都要定义变量,一些代码是用来注释的,变量和变量之间有一些关系,要做一些运算,运算离不开流程控制,进行运算的数据往往来自数据结构,最基本的是数组。
所有编程语言的需要:
- 变量
- 注释
- 运算符
- 流程控制
- 数组
编码规范:命名、换行、缩进、分号 变量
在java中定义变量,必须要使用数据类型来声明。Scala中不用,只需要使用两个关键字来声明即可,var、val。
scala> var a = 1 a: Int = 1 scala> var b ="1" b: String = 1 scala> var c = 2.0 c: Double = 2.0
重要结论1:Scala在定义变量的时候,可以不用指定数据类型,当然也可以指定,如果不指定,编译器会自动推断。 不确定数据类型,不要指定数据类型,类型不匹配会报错
重要结论2:如果已经定义好的变量,就算定义时没有指定类型也一定已经确定了,这个使用var修饰的变量可以被改成相同类型的其他值,但是不能改成其他类型的值。
重要结论3:如果已经有一个变量 a:Int = 3,那么使用var a = "huangbo"这是可以执行的。var的作用就是告诉编译器,正在定义一个新的变量。
重要结论4:一个变量被val修饰,这个变量等同于java中的常量,就是使用final修饰的变量。
重要结论5:如果一个变量被lazy修饰,延迟初始化,在定义的时候没有初始化,在使用的时候才被初始化。
重要结论6:在scala中,推荐使用val,不推荐使用var 为了类型和数据安全 scala是纯面向对象,任何东西都是对象,方法、函数都是对象 在java中,int a = 1 a是一个基本类型,但有封箱操作 数据类型 Scala和Java一样,有7种数值类型Byte、Char、Short、Int、Long、Float和Double(无包装类型)和一个Boolean类型,再加上String类型。
Any是所有类的父类,Nothing是所有类的子类 值类型:Double、Float、Long、Int、Short、Byte、Unit、Boolean、Char
Unit是特殊的类型,有且只有一个实例对象,就是个小括号 Unti: (),相当于Java中的void, 没有任何实质意义 因此,Scala中所有的方法都有返回值 Int:1 2 3....
上图各类型之间的虚线,从上到下可以进行转换,隐式转换,Scala编译器自动进行 引用类型:父类是AnyRef,等同于Java中的object
String是引用类型的一个特例
List Seq Iterable ScalaObject 所有Java类型都是AnyRef的子类
所有引用类型都有子类Null,即Null是所有引用类型的子类 Null也只有一个实例,是null,相当于Java中的null,能赋值给任何引用类型变量,不能赋值给值类型变量 基本类型操作
编码规范
1.不强制在代码末尾使用分号,但是如果有多句代码写在同一行,必须使用分号隔开
2.注释方式和Java一样
3.关键字 4.命名和Java一样
流程控制-if 分支控制
new没有scala class,Libraries添加
scala> var result = 2; 3 <console>:11: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses var result = 2; 3 ^ result: Int = 3 scala> var x =1; var y =2 x: Int = 1 y: Int = 2 scala> var result = if(x > y) println(x) else println(y); 4 2 result: Int = 4
大括号是有返回值的,里面的代码块最后一句是返回值,赋值给了result
scala> var result = if(x > y) println(x) else println(y); 4 2 result: Int = 4 scala> var result = if(x > y) println(x) else println(y) 2 result: Unit = () scala> var result = if(x > y) x else y result: Int = 2
在Scala中任何一句代码的执行都是有返回值的,下面代码返回的是Unit,这相当于Java里的无返回值
scala> var result = if(x > y) println(x) else println(y)
2
result: Unit = ()
代码的逻辑块: 函数 val sum = (x:Int , y:Int) => x + y 方法 def sum(x:Int, y:Int) = x + y 代码段/代码块
函数和方法都会变成代码块的执行结果,接收的都是最后一句的值
如果大括号只有一句代码,这一句代码的值就是的返回值
if(x > y) x else y 是有返回值的,因此可以进行赋值
var result = if(x > y) x else y
即var result = if(x > y) x else y
Scala中任何一句能正常执行的代码都有返回值
如果要返回的是一段代码的结果,那么请把这一段代码放置在中,那么中最后一句话的结果就是的返回值
scala> var a = 2 a: Int = 2 scala> var b = "h" b: String = h scala> var result = if(a > 0) a else b result: Any = 2
返回的类型是父类Any
scala> var a = 2 a: Int = 2 scala> var b = 1.0 b: Double = 1.0 scala> var result = if(a > 0) a else b result: Double = 2.0
隐式转换,Int会转为Double,因此最终是Double类型
scala> var abc = if(a > 0) "abc" abc: Any = abc
a>0返回abc,否则返回Unit,因此最终是共同父类Any
流程控制-for
遍历/迭代
scala> var aa = 1 to 10 aa: scala.collection.immutable.Range.Inclusive = Range 1 to 10 scala> var bb = 1 until 10 bb: scala.collection.immutable.Range = Range 1 until 10
数组
scala> var array = Array(1, 3, 5, 7, 9) array: Array[Int] = Array(1, 3, 5, 7, 9) scala> var array = Array(1, 3, 5, 7, "A") array: Array[Any] = Array(1, 3, 5, 7, A)
for遍历数组
val array = Array(1, 2, 3, 4, 5, "hello") for (a <- array) println(a)
Java和Scala对比 可以利用to来取数组,Scala中用下标取数组用小括号
val array = Array(1, 2, 3, 4, 5, "hello") for (i <- 0 to array.length - 1) println(i, array(i))
(0,1)
(1,2)
(2,3)
(3,4)
(4,5)
(5,hello) 或
val array = Array(1, 2, 3, 4, 5, "hello") for (i <- 0 until array.length - 1) println(i, array(i))
打印偶数位置上的元素
val array = Array(1, 2, 3, 4, 5, "hello") for (i <- 0 until array.length) if (i % 2 == 0) println(i, array(i))
相当于
val array = Array(1, 2, 3, 4, 5, "hello") for (i <- 0 until (array.length, 2)) println(i, array(i))
步长为2,隔一个打印一次
scala> var abc = Array(1, 2, 3, 4, 5) abc: Array[Int] = Array(1, 2, 3, 4, 5) scala> for(a <- 0 until abc.length) yield abc(a) * 2 res1: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
上面将所有循环的结果组合在一起,成为一个新的结果
scala> for(x <- 0 until a.length; y <- 0 until b.length if x != y)println(a(x), b(y)) (1,b) (1,c) (2,a) (2,c) (3,a) (3,b)
双重循环 if 守卫
相当于Java中的 总结: 1、在Scala中没有运算符,所有的符号其实都是方法。 1+2 1.+(2)
2、在Scala中没有++、--的用法
3、for(i <- 表达式/数组/集合)
4、在for循环里面可以添加if表达式
5、有两个特殊表达式要了解: To Until
6、如果在使用for循环的时候,需要获取,yeild关键字 while 循环 和Java是一样的
方法和函数
scala> def sum(x:Int, y:Int):Int = x + y
sum: (x: Int, y: Int)Int
def 方法名(参数列表):返回值类型 = 方法体
代码块里只有一句可以省掉大括号 定义方法 函数的定义
val sum = (x:Int, y:Int) => x + y val multiple = (x:Int, y:Int) => x*y println(sum(2, 3)) println(multiple(2, 3))
val 函数名 = (参数列表) => 代码块
函数相当于变量,方法也相当于变量
=>可以理解为“转化为/变为” 上下对比 f 相当于变量名
Int => Int相当于类型
<function> 相当于值
下面方法第二个参数是一个函数 f: Int => Int 接收Int类型返回Int类型
def m1(x:Int, f: Int => Int)= f(x) val ff1 = (x: Int) => x + 1 val ff2 = (x: Int) => x * 2 println(m1(1, ff1), ff1(1)) println(m1(1, ff2), ff2(1))
(2,2)
(2,2)
方法可以做为方法的参数
方法可以做为函数的参数
函数可以做为方法的参数
函数可以做为函数的参数
集合相关
- 数组(Array)
- 集合(Seq序列/列表, Set集合, Map映射)
很多其他的数据结构:Tuple, Stack,Queue,List
数组Array 分成两大类 1.定长数组和变长数组
- 定长数组:Array(insert,delete是没有的,只有修改操作),当然还能模拟出插入和删除操作
- 不定长数组:ArrayBuffer 缓冲数组,没有指定数组长度,可以无限增加元素
scala> val array1 = Array(1, 2, 3)
array1: Array[Int] = Array(1, 2, 3)
scala> array1(1)
res3: Int = 2
scala> array1(2)
res4: Int = 3 // 转为变长数组
scala> val ab = array1.toBuffer
ab: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3) // 追加一个元素
scala> ab += 1 res5: ab.type = ArrayBuffer(1, 2, 3, 1) // 追加多个元素
scala> ab += (2, 3, 4, 5)
res6: ab.type = ArrayBuffer(1, 2, 3, 1, 2, 3, 4, 5) // 追加一个数组
scala> ab ++= Array(6, 7)
res8: ab.type = ArrayBuffer(1, 2, 3, 1, 2, 3, 4, 5, 6, 7) // 在数组某个位置插入元素用insert
scala> ab.insert(0, -1, 0) // 删除数组某个位置的元素用remove
scala> ab.remove(8, 2) //追加一个数组缓冲
scala> ab ++= ArrayBuffer(9, 10)
res15: ab.type = ArrayBuffer(-1, 0, 1, 2, 3, 1, 2, 3, 6, 7, 9, 10)
+= 表示追加一个元素或多个元素
++= 表示追加一个Array或者ArrayBuffer或者其他的Seq类型的实现类的对象
ab.insert(0, -1, 0) 在第0位置上插入-1和0
ab.remove(0, 2) 从第0位置开始删除两个元素
scala> val arr1 = Array(1, 2, 3) arr1: Array[Int] = Array(1, 2, 3) // 用new,开辟空间,数组元素全为0 scala> val arr2 = new Array[Int](3) arr2: Array[Int] = Array(0, 0, 0) // 没用new,数组中有一个元素为3 scala> val arr3 = Array[Int](3) arr3: Array[Int] = Array(3) scala> val arr4 = Array.apply(3) arr4: Array[Int] = Array(3)
要点:如果在构造数组的时候带了new,表示申请一个固定长度的数组
如果没有使用new,其实也在构造数组,只不过调用的是Array.apply()
定长数组和不定长数组的转换
val aa = Array(1, 2, 3)
val bb = new ArrayBuffer[Int]()
定长数组 aa.toBuffer 变长数组
变长数组 bb.toArray 定长数组
数组的常用操作
scala> ab.max res16: Int = 10 scala> ab.min res17: Int = -1 scala> ab.sum res18: Int = 43 scala> ab.toArray res19: Array[Int] = Array(-1, 0, 1, 2, 3, 1, 2, 3, 6, 7, 9, 10) scala> ab.mkString("_") res20: String = -1_0_1_2_3_1_2_3_6_7_9_10 scala> ab.mkString("<", "-", ">") res21: String = <-1-0-1-2-3-1-2-3-6-7-9-10>
map
scala> ab.map((x:Int) => x + 1)
res22: scala.collection.mutable.Buffer[Int] = ArrayBuffer(0, 1, 2, 3, 4, 2, 3, 4, 7, 8, 10, 11)
参数是个匿名函数
map:不停的从数组中循环出来一个元素,交给参数函数做为输入,参数函数执行完毕后得到一个新的结果:map方法的执行逻辑还会初始化一个新的同类型的定长数组做为新的元素的返回容器
scala> val func1 = (x: Int) => x * x func1: Int => Int = $$Lambda$1314/1216947434@704f5049 scala> ab.map(func1) res23: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 0, 1, 4, 9, 1, 4, 9, 36, 49, 81, 100) scala> val func2 = (x:Double) => x * x func2: Double => Double = $$Lambda$1315/2024365811@6f04b51b scala> ab.map(func2) <console>:15: error: type mismatch; found : Double => Double required: Int => ? ab.map(func2)
1)ab数组中的类型为Int类型,因此传入的参数函数的传入参数类型也必须是Int类型,其他不行
2)由于是map逻辑,所以这个参数的函数,只能定义一个参数,不能定义两个参数或多个参数
3)函数返回值类型没有限制
reduce
用来聚合
scala> val aa = Array(3, 4, 5, 6, 7, 66, 77, 11, 22) aa: Array[Int] = Array(3, 4, 5, 6, 7, 66, 77, 11, 22) scala> aa.max res25: Int = 77 scala> aa.min res26: Int = 3 scala> aa.reduce((x:Int, y:Int) => if(x > y) x else y) res27: Int = 77 scala> aa.reduce((x:Int, y:Int) => if(x < y) x else y) res28: Int = 3 // 相当于aa.sum scala> aa.reduce((x:Int, y:Int) => x + y) res29: Int = 201
reduce方法的逻辑:
给定一个初始变量(初始变量的类型是否可以推断?)
从aa中每次拿出一个元素和这个初始值进行聚合(是怎样的逻辑就由参数函数的实现体来决定)
聚合完成之后得到元素就重新赋值给这个临时变量
然后重复执行聚合直到最后得到一个元素
reduce方法的参数有要求:
1)既然是聚合,所以这个参数,必定是接收多个参数,返回较少的值,一般来说,就是接收两个参数,返回一个值
2)参数函数的类型依然要和aa中的元素一致
3)参数函数的返回值类型,必须和参数函数的输入参数的类型一致,因为返回值会在聚合中成为输入参数
count
scala> aa.map((x:Int) => 1).reduce((x:Int, y:Int) => x + y)
res30: Int = 9
将元素变为1然后相加
flatMap fold avg
作业:
求均值
用Java模拟map操作
用Java模拟reduce操作
aa.filter((x:Int) => if (x % 2 != 0) true else false)
数组的遍历
for(a <- aa) println(a) for(i <- 0 until aa.length) println(aa(i))
集合
Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展字Iterable特质,在Scala中集合有可变(mutable)和不可不(immutable)
两种类型,immutable类型的集合初始化后就不能改变了(注意与val修饰的变量进行区别)
可变和不可变
可变:当前这个集合的元素可以改变
不可变:当前这个集合中的元素不可更改 var和val 1)scala推崇使用val而不推荐使用var:为了安全 2)var表示可变,val表示不可变 (val == final)
他们修饰的变量是:
基本类型:值不可以改
引用类型:引用不可以改
final int a = 2
a不能改为其他值
final Student s = new Student("xiaoming", 22)
s的年龄可以改为其他值
val list = scala.collection.immutable.List(1, 2, 3) 也可以模拟一些修改操作,但是返回的都不是原来的集合,都是一个新的集合
https://blog.csdn.net/zhongqi2513/article/details/82956813 Scala集合的mutable和immutable解释
四种常用的集合类:List Set Map Tuple + Array
序列-List
任何一个List都是由 头元素 和 尾列表 组成
空列表:Nil 避免空指针的异常
::操作符将给定的头和尾创建一个新的列表,是右结合的
scala> val list2 = 3 :: Nil
list2: List[Int] = List(3)
头元素::尾列表
scala> val list3 = 4 :: 3 :: Nil
list3: List[Int] = List(4, 3)
先是3和空的尾列表Nil结合,再是4和尾列表List(3)结合
scala> val list1 = List(1, 3, 4) list1: List[Int] = List(1, 3, 4) scala> val list2 = 2 :: list1 list2: List[Int] = List(2, 1, 3, 4) scala> val list3 = list1 :: 2 <console>:13: error: value :: is not a member of Int val list3 = list1 :: 2 ^ scala> val list3 = list1 :+ 2 list3: List[Int] = List(1, 3, 4, 2) scala> val list4 = 2 +: list1 list4: List[Int] = List(2, 1, 3, 4) // 相当于2 +: list1 scala> val list5 = list1.+:(2) list5: List[Int] = List(2, 1, 3, 4) // 相当于list1 :+ 2 scala> val list5 = list1.:+(2) list5: List[Int] = List(1, 3, 4, 2) scala> val list3 = list1 +: List(2) list3: List[Any] = List(List(1, 3, 4), 2) scala> val list3 = List(2) :+ list1 list3: List[Any] = List(2, List(1, 3, 4))
:+和+: 两者的区别在于:+方法用于在尾部追加元素,+:方法用于在头部追加元素,和::很类似,但是::可以用于pattern match ,而+:则不行. 关于+:和:+,只要记住冒号永远靠近集合类型就OK了。
比如 2 :+ list1 会出错,因为2不是集合类型,写成List(2) :+ list1就不报错了
// 两个list合并成一个新的list scala> val list4 = list1 ++ list2 list4: List[Int] = List(1, 3, 4, 2, 1, 3, 4) // list2插入到list1前面生成一个新的集合 scala> val list6 = list1.:::(list2) list6: List[Int] = List(2, 1, 3, 4, 1, 3, 4)
scala> val nums = 1::2::3::4::Nil nums: List[Int] = List(1, 2, 3, 4) // 判断List是否为空 scala> nums.isEmpty res34: Boolean = false // 头部元素 scala> nums.head res36: Int = 1 // 尾列表 scala> nums.tail res37: List[Int] = List(2, 3, 4) // 第二个元素 scala> nums.tail.head res38: Int = 2 // 头部列表 scala> nums.init res39: List[Int] = List(1, 2, 3) // 最后一个元素 scala> nums.last res40: Int = 4 // scala> val nums2 = nums.init :+ nums.last nums2: List[Int] = List(1, 2, 3, 4) // 倒数第二个元素 scala> nums.init.last res41: Int = 3 // 反转列表 scala> nums.reverse res42: List[Int] = List(4, 3, 2, 1) // 第二个元素 scala> nums.init.reverse.tail.init res43: List[Int] = List(2) // 舍掉第一个元素 scala> nums.drop(1) res44: List[Int] = List(2, 3, 4) // 舍掉前两个个元素 scala> nums.drop(2) res45: List[Int] = List(3, 4) // 取前两个元素 scala> nums take 2 res47: List[Int] = List(1, 2) // 分割列表 scala> nums.splitAt(2) res48: (List[Int], List[Int]) = (List(1, 2),List(3, 4)) // 分割列表,返回的是元组 scala> nums.splitAt(3) res49: (List[Int], List[Int]) = (List(1, 2, 3),List(4)) // 取元组的第一个元素 scala> res49._1 res50: List[Int] = List(1, 2, 3) // 取元组的第二个元素 scala> res49._2 res51: List[Int] = List(4) scala> val chars = List("a", "b", "c", "d") chars: List[String] = List(a, b, c, d) // zip,返回元组的列表 scala> nums zip chars res52: List[(Int, String)] = List((1,a), (2,b), (3,c), (4,d)) // 转为映射 scala> res52.toMap res53: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b, 3 -> c, 4 -> d) // 转为字符串 scala> nums.toString res54: String = List(1, 2, 3, 4) // 转为数组 scala> res52.toArray res55: Array[(Int, String)] = Array((1,a), (2,b), (3,c), (4,d))
Set
集合:最大的作用是,去重,求交集并集差集
package com.jiehui.test import scala.collection.immutable.HashSet import scala.collection.mutable object Demo_If def main(args: Array[String]): Unit = // 不可变set val set1 = new HashSet[Int]() println(set1) // Set() // set1不会变 val set2 = set1 + 4 println(set1, set2) // (Set(),Set(4)) // ++ 两个集合合并,元素顺序不一定,hash值决定 val set3 = set2 ++ Set(5, 6, 7) val set4 = Set(1, 3, 2) ++ set2 println(set3, set4) // (Set(5, 6, 7, 4),Set(1, 3, 2, 4)) val set5 = Set(1, 2, 3, 4, 5) val set6 = Set(3, 4, 5, 6, 7) // 交 println(set5.intersect(set6)) // Set(5, 3, 4) // 并 println(set5.union(set6)) // Set(5, 1, 6, 2, 7, 3, 4) // 差 println(set5.diff(set6)) // Set(1, 2) // 可变set val set7 = new mutable.HashSet[Int]() val set8 = set7 + 1 println(set8, set7) // (Set(1),Set()) set7 += 1 println(set7) // Set(1) set7 ++= Set(2, 3, 5) println(set7) // Set(1, 5, 2, 3) set7 -= 5 println(set7) // Set(1, 2, 3) set7.remove(1) println(set7) //Set(2, 3)
两大类:
1、构建,增加,删除
2、求交集,并集,差集
Map
两大类:
1、创建map,put(k, v),get(k)
2、其他常用操作
scala> val map = Map("a" -> 1, "b" -> 2) map: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2) scala> map res56: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2) scala> map("a") res57: Int = 1 scala> map.get("a") res58: Option[Int] = Some(1)
有三种需要注意的类型:
Option 父类
两个子类:
Some 表示有值
None 表示无值
scala> map("c") java.util.NoSuchElementException: key not found: c at scala.collection.immutable.Map$Map2.apply(Map.scala:138) ... 28 elided scala> map.get("c") res60: Option[Int] = None
用get不会报错,避免了key不存在抛出异常的情况
scala> map.getOrElse("c", 2)
res61: Int = 2
可变的map
scala> val map1 = new scala.collection.mutable.HashMap[String, Int]() map1: scala.collection.mutable.HashMap[String,Int] = Map() scala> map1("c") = 3 scala> map1("a") = 1 scala> map1("b") = 2 scala> map1 res65: scala.collection.mutable.HashMap[String,Int] = Map(b -> 2, a -> 1, c -> 3) scala> map1("c") = 5 scala> map1 res67: scala.collection.mutable.HashMap[String,Int] = Map(b -> 2, a -> 1, c -> 5)
scala> val map1 = new scala.collection.mutable.HashMap[String, Int]() map1: scala.collection.mutable.HashMap[String,Int] = Map() scala> map1("c") = 3 scala> map1("a") = 1 scala> map1("b") = 2 scala> map1 res65: scala.collection.mutable.HashMap[String,Int] = Map(b -> 2, a -> 1, c -> 3) scala> map1("c") = 5 scala> map1 res67: scala.collection.mutable.HashMap[String,Int] = Map(b -> 2, a -> 1, c -> 5) scala> map1 += (("c", 5), ("d", 5)) res68: map1.type = Map(b -> 2, d -> 5, a -> 1, c -> 5) scala> map1 -= "c" res69: map1.type = Map(b -> 2, d -> 5, a -> 1) scala> map1.remove("a") res70: Option[Int] = Some(1)
key一般倾向使用不可变的类型 final String
Tuple
list set 定义了之后,能放进去的类型就确定了 tuple 能组合多个不同类型的值到一起,形成一个不可变的一个数据整体 访问的时候通过顺序编号值 . _x
scala> val t = ("shu", 2, false, 4.4) t: (String, Int, Boolean, Double) = (shu,2,false,4.4) scala> t._1 res71: String = shu scala> t._2 res72: Int = 2 // 只有一个元素时,不是元组,是一个值 scala> val tt = (1) tt: Int = 1 scala> val (a, b, c, d) = ("shu", 2, false, 4.4) a: String = shu b: Int = 2 c: Boolean = false d: Double = 4.4 scala> val t,(a, b, c, d) = ("shu", 2, false, 4.4) t: (String, Int, Boolean, Double) = (shu,2,false,4.4) a: String = shu b: Int = 2 c: Boolean = false d: Double = 4.4
为什么元组是四种类型最常用的呢?
元组的拉链操作
仅限于二元组
元组中的元素的个数只有两个,就可以看作是二元组,用途 转key-value
以上是关于Scala基础的主要内容,如果未能解决你的问题,请参考以下文章