Scala学习笔记(一)
1.Scala概述
1.1.什么是Scala
- Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于java虚拟机,并兼容现有的java程序。
Scala与java及jvm关系:
scala与java都运行在jvm上。
java:xxx.java --编译 javac--》 xxx.class --运行 java--》
scala:xxx.scala --编译 scalac--》 xxxx.class --运行 scala--》 //都是编译成class文件
其实JVM这种执行引擎非常的通用,有很多编程语言都是基于此。kotlin Python ruby
-
Scala官网:Scala官网
-
Scala语言是一种以java虚拟机jvm为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言(静态语言需要提前编译,如java,c,c++,动态语言无需提前编译,如js)。能在java和.Net平台上运行,既可以用于大规模应用程序开发,也可用于脚本编程。
scala可以直接调用现有的java类库,实现两种语言的无缝对接。
- 注意三者区别:
面向对象编程,面向过程编程,函数式编程。
编程语言之分:
汇编语言,脚本语言,机器语言,高级语言。
静态编程语言和动态编程语言
编译型和解释型
面向对象和函数式编程
强类型语言和弱类型语言
举例:
Python是动态类型语言,是强类型语言,解释型编程语言。
JS是动态类型语言,是弱类型语言,解释型编程语言。
java是静态类型语言,是强类型语言。
GO是编译型语言,是静态类型语言。
1.2 为什么要学习Scala
第一,基于编程本身:优雅
这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。
速度快
Scala语言表达能力强,一行代码抵得上java多行,开发速度快;Scala是静态编译的,所以和JRUBY,Groovy比起来速度会快很多。
能融合到hadoop生态圈
hadoop现在是大数据实施标准,spark或者flink的出现并非是要取代hadoop,而是要完善hadoop生态圈。jvm语言大部分可能会想到java,但java做出来的API太丑,或者想实现一个优雅的API太费劲。
第二,基于活跃度:
- Spark和flink的源码都使用Scala编写。
- Scala将成为未来大数据处理的主流语言。
- Tiobe编程语言排行榜,Scala比较靠前。Tiobe编程语言排行榜
2. Scala编译器安装
2.1. 安装JDK1.8
因为Scala是运行在JVM上的,所以安装Scala之前先安装jdk
2.2. Scala的sdk下载,配置scala的环境变量。
- 官网下载地址:scala下载地址
- linux环境下的安装步骤:
上传压缩包到服务器,创建Scala文件夹。
解压Scala安装包,tar -zxvf scala-2.11.8.taz -C /usr/local/scala/
修改配置文件 vim /etc/profile
export SCALA_HOME=/usr/local/scala/scala-2.11.8
export SCALA_HOME=$PATH:$SCALA_HOME/bin
:wq保存
source /etc/profile,使环境变量生效。
SCALA_HOME=D:\scala
PATH=%SCALA_HOME%\bin
验证:
输入scala,进入交互式命令行。
val a = 10
解释:a: Int =10
打印输出:
println("hello world")
退出命令行:
:quit 冒号不可少。
- idea集成Scala插件:
第一种,在首页点击Configure选择plugins,搜索Scala插件,点击install,安装完成后重启idea即可生效。
第二种,在本地安装,点击齿轮选择install plugin from disk。选择下载好的插件scala-intellij-bin-2018.1.3.zip(最好和idea版本号对应)
configure--》plugins--》install plugin from disk--》选择Scala插件--》OK--》重启idea
3.Scala基本语法
//先看java语法
public class HelloWorld{
public static void main(String[] args){
System.out.println("hello world");
}
}
//对比Scala语法
//def 用来定义方法的关键字
// main 方法名
// args 形参名称
// Array[String] args这个形参的数据类型,String类型的数组,相当于java中的String[]
//:Unit 方法的返回值
//例如:def max(a:Int,b:Int):Int = {}
object HelloScalaXXXX{
def main(args: Array[String]): Unit = {
println("hello scala")
}
}
代码举例:(注意区分String和Char)
val xxxl = 'a'//Char
val xxxxl = "a"//String
var a = true//Boolean
var bb = ()//表示没有类型,等同于bb:Unit =()
idea中创建Scala class文件
在idea中编写Scala程序,点击project structure--》global libraries -》点击+号添加Scala-sdk,将其添加到工程muddle中。
在项目中创建Scala文件夹,右键选择Mark Directory as --》Sources Root。右键new 创建Scala class文件。
核心要点:
1. 类名和文件名(scala中不一定要一致。
但是java中public修饰的类必须和文件名一致,且java一个文件中只能有一个public类。)
2. main方法的定义(注意和java的main方法对比理解)
3. 分号(可有可无)
4. 编译和执行(scalac 和 scala)
5. 类的声明(object和class),注意object首字母小写。
变量的定义:
object VariableDemo{
def main(args: Array[String]){
//val修饰的变量值不可变,相当于java中的final
val i =1;
//使用var修饰的变量值可变,在scala中鼓励使用val
var s = "hello";
//Scala会自动推断变量的类型,必要的时候可以指定类型
//变量名在前,变量在后
val str:String = "spark";
}
}
总结:
1.数据类型可以指定,也可以不指定,如果不指定,那么scala就会进行自动推断。
2.如果指定数据类型,数据类型的指定方式是在变量名后面加一个冒号,然后写上数据类型。
3.scala里面的变量修饰符一共两个,var和val。var修饰的变量的值可以修改,但是不可以修改成其他类型的值。val修饰的变量不可以修改值。
思考:
val a : Int =3
val a: Int =4;可以修改吗?
(可以修改)
懒加载:
lazy val abc = {3+3} //abc:Int=<lazy>
println(abc) //6
4.scala数据类型
- java中有基本数据类型和包装类型,但是scala中没有这种说法。
- scala和java一样,有7种数值类型,Byte,Char,Short,Int,Long,Float和Double(无包装类型)和一个Boolean类型,再加上常用的String类型。
- 注意:scala中没有基本数据类型和包装类型的说法,如果非要定义的话,可以认为scala所有的类型都是包装类型。
数据类型 描述
Byte 8位
Short 16位
Int 32位
Long 64位
Float 32位单精度
Doulbe 64位双精度
Char 16位
String 字符序列
Boolean true或false
Unit 表示无值,相当于void,用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写作()。
Null null或空引用
Nothing Nothing类型在scala的类层级的最低端;它是任何其他类型的子类型。
Any Any是所有其他类的超类。
AnyRef AnyRef类是scala所有引用类(reference class)的基类。
4.1总结:
- Any是所有类的父类,包括值类型AnyVal和引用类型AnyRef。
- AnyVal是所有值类型的父类,包括Int,Double,Boolean,Unit等等。
- AnyRef是所有引用类型的父类,包括Null。
- Null是所有引用类型的子类。
- Nothing是所有类的子类。
- Unit类型只有一个实例,是(),相当于java中的void,没有任何的实质意义。
- Null也只有一个实例,是null,相当于java中的null,能赋值给任何引用类型变量,不能赋值给值类型变量。
4.2 scala基本类型操作
- 算术操作:+ - * / %
- 关系运算: >,>=,<,<=,==,!=
- 逻辑运算:&& || !
- 位运算:&,|,^,~,>>,<<,>>>
- 对象比较:11,11.0,“zsk”==“zsk”,ne,eq
- 特别注意:ne ,eq ,equals ,==四者的区别。
4.3 scala编码规范
- 分号:在scala编码中,不强制在末尾加分号,如果多句代码在同一行,则必须加分号隔开。
- 注释:与java注释规则一样,//单行,/多行/。
3.变量名命名:驼峰式。 - 关键字:主要关注新关键字:yield,match,object,def,implicit,trait,sealed,var/val,lazy,forSome
5.流程控制
//if、for、while、continue、break;
val x = 1 //x:Int=1
val y = if(x>1) 1 else -1 //y:Int=-1
if(x>1){print("1")}else{print("2")} //2
val x=1 //x:Int = 1
val x = {1} //x:Int = 1
val x = {print("2222");2}//打印2222,x:Int=2,x的值为2
val x= {print("2222");val a = 2+2+2; a}//打印2222,x:Int=6,x的值为6
思路为当前代码块{}的最后一行就是一个变量,这个变量的值就作为当前{}的返回值。若是写在代码体中,则最后一行是return a;
val x={var z = if(x>1) 1 else -1;z} //y:Int = 1
var z1 = if(x>1) 1 else "null" //z1 : Any=1,返回类型为Int和null的共同父类Any
var z1 = if(x>1) 1 else true //z1:AnyVal = 1,返回类型为Int和Boolean的共同父类AnyVal
var z1 = if(x>1) 1 //z1 : AnyVal = 1,相当于var z1 = if(x>1) 1 else (),返回类型为Unit和Int的共同父类AnyVal.
多行分支如下:
var score = 80
var result = if(score >=90) "A" else if(score <70) "C" else "B" //result: String = C
总结:
- if条件表达式是有返回值和返回类型的,返回值类型是多个分支的返回结果的共同父类。
- 返回值会根据条件表达式的情况自动进行数据类型的推断(自动推断的是多个返回结果的共同父类)。
5.2 块表达式
object BlockExpresstionDemo{
def main(args: Array[String]){
val x =0
//在scala中{}可包含一系列表达式,块中最后一个表达式的值就是块的值。如下是一个块表达式:
val result = {
if(x<0){
-1
}else if(x>=1){
1
}else{
"error"
}
}
//result的值就是块表达式的结果
println(result)
}
}
其他情况举例:
val r1 ={} //r1:Unit = ()
val r2 ={2} //r2: Int = 2
val r3 = {println("hello");2} //hello;r3:Int = 2
val r4 = {println("hello")} //hello;r3:Unit =(),最后一行没有参数时,返回值为Unit的实例()
val a = {val b=2} //a: Unit = (),代码块中也可以接变量定义表达式
5.3 for
for的语法结构:
java:
for(int i =0;i<=10;i++)
scala:
for(i <- 表达式/数组/集合),注意<-之间没有空格。
Array[Int] array1 = Array(1,2,3,4,5,6)
for(i <- array1){println(i)}
to语法:
1 to 10 ,左闭右闭区间,scala.collection.immutable.Range.Inclusive = Range(1,2,3,4,5,6,7,8,9,10)
1 until 10 ,左闭右开区间 ,scala.collection.immutable.Range = Range(1,2,3,4,5,6,7,8,9)
//scala中也可以通过角标访问数组元素
Array[Int] array1 = Array(1,2,3,4,5,6)
array1(0) //res30: Int = 1
//由此引出另外一种遍历方式
for (i <- 0 to array1.length - 1){println(array1(i))}
//to 的另一种用法 start to (end,step)
2 to (15,4) //(2,6,10,14)
//倒叙的话可以把步长改为-1,从大到小开始
(array1.length-1) to (0,-1) //(6,5,4,3,2,1)
//利用循环打印
for(i <- (array1.length-1) to (0,-1)) {println(array1(i))} //(6,5,4,3,2,1)
//双重for循环加条件
for(i <- "abc";m<-"xbz" if i != m) {println(i + "*" + m)}
5.4while语法
object whileDemo{
def main(args: Array[String]):Unit{
//定义起始条件
var n=10;
//终止条件
while(n>0){
println(n)
//迭代条件
n -= 1
}
}
}
总结:
- while使用跟java一模一样
- 注意点:scala里面不支持 i++ i--操作,统一写成i+=1,i-=1
5.5break和continue
- scala中没有break和continue关键字,scala推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。
- break:跳出整个循环,continue:跳出当前循环。
import util.control.Breaks._//下划线表示全部,相当于java中的*
object Demo007_Break_Continue{
def main(args: Array[String]):Unit{
//break举例
breakable(
for(i <- 1 to 5){
if(i == 3){
break()
}
println(i)
}
)
//continue举例,将breakable嵌套在for循环内部,达到continue的效果
for(i <- 1 to 5){
breakable{
if(i == 3){
break
}
println(i)
}
}
}
}
6.方法和函数
例一:
1 to 10//生成一个1到10的序列
相当于1.to(10)//同样是1到10的序列
例二:
var a =1
var b=10
a.to(b)//也是1到10的序列
例三:
1+10相当于1.+(10),简单来说,在scala中所有操作符号都是函数和方法
6.1定义方法
//def,定义一个方法
//max,方法名称
//a,b,参数列表
//:Int,方法类型,可以省略,如果不写会自动进行类型推断
//=,等号不可丢
def max(a: Int,b: Int): Int = {
if(a>b){
a
}else{
b
}
}
6.2定义函数
//min,函数名
//(Int,Int) => Int,函数的类型,两个Int转成一个Int
//=, 赋值等号
//(x,y) => if(x > y) y else x, 函数的实现逻辑
//=>,转变成或者计算成的意思
val min:((Int,Int) => Int) = (x,y) => if(x > y) y else x
第二种写法:
val min1 = (x:Int,y:Int) => if(x>y) y else x
类比写法:把函数理解成普通的变量。
函数其实也是一种类型 function2 aa:((Int,Int) => Int)
Int a:Int
val a:Int = 3
val aa:(Int => Int) = {x => x+1}
函数是scala中的一等公民
函数的意义:表示接受两个Int类型的变量,然后做累加。
经过scala的自动类型推断得知,最后返回的结果数据的类型也是Int
Function2中的2表示这个函数接收的参数个数是2个。
6.3函数和方法的区别
- 函数可以作为参数传递给方法,也就是说函数可以作为方法的参数。在函数式编程语言中,函数是头等公民,它可以像任何其他数据类型一样被传递和操作。
案例:先定义一个方法, 再定义一个函数,然后将函数传递到方法里面。
def m2(f: (Int,Int)=>Int) = f(2,6)
val f2 = (x:Int,y:Int)=> x-y
m2(f2)
- 函数可以作为方法的参数,也可以作为函数的参数
//(f:Int => Int,x:Int) => f(x) +1是函数
//(f:Int => Int,x:Int)也是函数,但是作为上面函数的参数传递
val aa = (x:Int) => x+1
val bb = (f:Int => Int,x:Int) => f(x) +1
bb(aa,5)
- 方法也可以作为方法的参数。在需要传入函数作为参数的位置上传入一个方法的话,那么这个方法会被自动的转换为函数作为参数,也可以通过“_”把方法转为参数
//方法
def mm(x:Int,y:Int):Int = x+y
//方法,第一个参数是函数
def mm1(f:(Int,Int) => Int,x:Int,y:Int) = f(x,y)
//需要传入函数,但是传入方法会自动转换为函数
mm1(mm,2,4)
//通过“_”显示的转换成函数
mm1(mm _,2,4)
例一:
def add2(x:Int,f:((Int,Int) => Int)) = (z:Int,y:Int) => x + f(y,z)
val result1 = add2(4,(a:Int,b:Int) => a+b)
val result2 = result1(2,3)//9
例二:
def add3(x:Int,f:Int=>Int) = (y:Int) => f(y) + x
val f22 = (q:Int)=> q*q
val f33 = add3(10,f22)
f22(2)//4
f33(2)//14
综合测试:
object Demo09MethodFunction{
//定义一个方法,方法m1参数要求是一个函数,函数的参数必须是两个Int类型,返回值类型也是Int类型
def m1(f:(Int,Int)=>Int):Int = f(2,6)
//定义一个需要两个Int类型参数的方法
def m2(x:Int,y:Int):Int = x+y
//定义一个计算数据不被写死的方法
def m3(f:(Int,Int) => Int,x:Int,y:Int):Int = f(x,y)
//定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
val f1 = (x:Int,y:Int) => x+y
//定义一个函数f2
val f2 = (m:Int,n:Int) => m*n
//定义一个传入函数的函数
val f3 = (f:(Int,Int) => Int,x:Int,y:Int) => f(x,y)
//main方法
def main(args: Array[String]):Unit{
//调用m1方法,并传入f1函数
val r1 = m1(f1)
println(r1)
//调用m1方法,并传入f2函数
val r2 = m1(f2)
println(r2)
//调用m3方法,并传入f1函数
val result1= m3(f1,2,4)
println(result1)
//调用m3方法,并传入f2函数
val result2 = m3(f2,2,4)
println(result2)
//调用m3方法,并传入m2方法
println(m3(m2,2,4))
//调用f3函数,传入f1函数
println(f3(f1,3,4))
}
}
6.4Z将方法转换成函数
def m1(x:Int,y:Int):Int = x*y
val f1 = m1 _ //下划线将m1这个方法转换成了函数
6.5 scala函数式编程特点
- 高阶函数(higher-order functions)
- 闭包(closures)
- 模式匹配(pattern matching)
- 单一赋值(single assignment)
- 延迟计算(lazy evaluation)
- 类型推导(type inference)
- 尾部调用优化(tail call optimization)
7.scala数组Array
7.1定长数组和变长数组
- 类似java中的string和stringBuffer,定长和变长。
- Array是不可变的(长度不可变),初始化之初就有了固定的长度,所以不能直接的对其元素进行删除操作,也不能多增加元素,只能修改某个位置的元素的值,要实现删除可以通过过滤生成新的Array的方式来删除不要的元素。所以也就没有add,remove,insert等操作。
- ArrayBuffer是可变的,本身提供了很多元素的操作,包括增删改查的操作。
- 如果需要array和arraybuffer之间互相转化,可以分别调用Array.toBuffer()和ArrayBuffer.toArray()方法。
object ArrayDemo{
def main(args: Array[String]){
//初始化一个长度为8的定长数组,其所有元素均为0
val arr1 = new Array[Int](8)
//直接打印定长数组,内容为数组的hashcode值
println(arr1)
//将数组转换成数组缓冲,就可以直接看到数组的内容了
//toBuffer会将数组转换为数组缓冲
println(arr1.toBuffer)
//注意,如果new会调用数组的apply初始化方法,代表传入的是长度
val a1 = new Array[Int](8)//(0,0,0,0,0,0,0,0)
val a2 = new Array[String](8)//(null,null,null,null,null,null,null,null)
//如果直接生成数组不会初始化
val a3 = Array[Int](8)//(8),如果有new,传入的是长度,如果没有new,则传入的是元素。
val a4 = Array[Int](8,9,10)//(8,9,10)
//使用arrayBuffer定义一个变长数组,导入import scala.collection.mutable.ArrayBuffer包
val ab = ArrayBuffer[Int]()
//向数组缓冲的尾部追加一个元素
ab += 1
//追加多个元素
ab += (2,3,4,5)
//追加一个数组++=
ab ++= Array(6,7)
//追加一个数组缓冲++=
ag ++= ArrayBuffer(8,9)
//打印数组缓冲ab
println(ab)
//在数组缓冲某个位置插入元素用insert
ab.insert(4,7)//在4的位置插入7
ab.insert(0,-1,0)
//删除某个位置的元素用remove
ab.remove(8,2)
//打印数组缓冲ab
println(ab)
}
}
7.2遍历数组
object ArrayForDemo{
def main(args: Array[String]): Unit{
//初始化一个数组
val arr = Array(1,2,3,4,5,6,7,8)
//增强for循环
for(i <- arr)
println(i)
//使用to可以生成一个序列作为脚标,to左闭右闭,until左闭右开
for(i <- (0 to arr.length -1).reverse)
println(i)
//util会生成一个range,reverse是将前面生成的range序列反转
for(i <- (0 until arr.length).reverse)
println(arr(i))
//步长为2
for(i <- (0 until (arr.length,2)).reverse)
println(arr(i))
}
}
7.3数组转换
- yield关键字将原始的数组进行转换会产生一个新的数组,原始的数组不变。
val arr = Array(1,2,3,4,5,6,7)//定义一个数组
val res = for(e <- arr) yield e * 2//用yield关键字生成一个新的数组(2,4,6,8,10,12,14)
arr.map(_ * 2)//用map方法得到新的数组(2,4,6,8,10,12,14)
完整用法:
val arr1 = Array(1,2,3,4,5,6,7)//定义一个数组
val res1 = for(e <- arr1 if e % 2 == 0) yield e * 10//将偶数取出,乘以10后组成新的数组
println(res1.toBuffer)
//高阶用法:
filter过滤,接受一个返回值为Boolean的函数,map相当于将数组中的每一个元素取出来,应用传进去的函数。
val r = arr.filter(_ % 2 == 0).map(_ * 10)
println(r.toBuffer)
7.4数组常用算法
val intArr = Array(1,2,3,4,5,6,7,8,9,10)
intArr.sum//55
intArr.max//10
intArr.min//1
intArr.toString()//地址
intArr.mkString(",")//1,2,3,4,5,6,7,8,9,10
intArr.mkString("<",",",">")//<,1,2,3,4,5,6,7,8,9,10>
7.5多维数组
var a1 = Array(1,2)
var a2 = Array(3,4)
var aa1 = Array(a1,a2)
aa1(0)(1)//2
for(i <- aa1) println(i.mkString(","))//1,2/n3,4
8.scala集合相关
8.1 概述
- scala的集合有三大类:序列Seq、集合Set、映射Map,所有的集合都扩展自Iterable特质
- 在scala中集合有可变(mutable)和不可变(immutable)两种类型,immutable类型的集合初始化后就不能修改(注意与val修饰的变量进行区别,immutable表示元素值不可变,val表示引用不可变,定长是指的数组的长度不可变。)
- 官网解释:scala集合分为两种,一种为可变一种为不可变的集合。可变的集合可以进行更新或删除,修改添加和删除作用于原集合。不可变集合一旦被创建就不可改变,添加删除更新操作返回的是新的集合,老集合保持不变。
- val和var,表明定义的变量(引用)是否能被修改而指向其他内容。
- immutable和mutable,表明的是内存中开辟出来的这块空间的内容能否被修改,如果针对immutable变量进行修改,其实是开辟了一块新的内存空间,产生了一个新的变量,而原来的变量依然没有改变。
8.2 常见的实现
- List:序列,类似于java中的list,元素有序,可以重复。
- Set:集合,类似于java中的set,元素无序,不重复。
- Map:映射,类似于java中的map,元素是key-value形式。
- Tuple:元组,类似于java中的一个自定义POJO类的简写形式。
8.3 list
- 不可变的序列:import scala.collection.immutable._
- 在scala中列表要么为空(Nil表示空列表),要么是一个head元素加上一个tail列表。
9 :: List(5,2), ::操作符是将给定的头和尾创建一个新的列表
注意,::操作符是右结合的,如9 :: 5 :: 2 :: Nil相当于9 :: (5 :: (2 :: Nil))
val list1 = Nil//表示一个空序列
var list2 = List(1,2,3,4)//(1,2,3,4)
var list3 = 33 :: list2//(33,1,2,3,4)
list2.head//1
list2.tail//(2,3,4)
list2.head :: list2.tail//(1,2,3,4)
//完整用法:
//创建一个不可变的集合
val lst1 = List(1,2,3)
//将0插入到lst1的前面生成一个新的List
val lst2 = 0 :: lst1
val lst3 = lst1.::(0)
val lst4 = 0 +: lst1
val lst5 = lst1.+:(0)
//将一个元素添加到lst1的后面产生一个新的集合
val lst6 = lst1 :+ 3
val lst0 = List(4,5,6)
//将两个list合并成一个新的List
val lst7 = lst1 ++ lst0
//将lst0插入到lst1前面生成一个新的集合
val lst8 = lst1 ++: lst0
//将lst0插入到lst1前面生成一个新的集合
val lst9 = lst1.:::(lst0)
println(lst9)
序列常用操作:
//采用::及Nil进行列表构建
val num2 = 1 :: (2::(3::(4 :: Nil)))//相当于List[Int] = List(1,2,3,4)
//由于::操作符的优先级是从右往左的,因此上一条语句等同于下面这条语句
val nums = 1 :: 2 :: 3 :: 4 :: Nil
//判断是否为空
nums.isEmpty
//取第一个元素
nums.head//1
//取除了第一个元素外剩余的元素,返回的是列表
nums.tail//(2,3,4)
//取列表第二个元素
num.tail.head//2
//List连接操作
List(1,2,3):::List(4,5,6)
//取除了最后一个元素外剩余的元素,返回的是列表
nums.init//(1,2,3)
//取列表最后一个元素
nums.last//4
//列表元素倒置
nums.reverse
//方法组合调用
nums.reverse.reverse == nums//true
nums.reverse.init//(4,3,2)
nums.tail.reverse//(4,3,2)
//丢弃前n个元素
nums drop 3//4
nums drop 1//(2,3,4)
//获取前n个元素
nums take 1//1
nums take 3//(1,2,3)
//将列表进行分割
nums.splitAt(2)//传一个下标,从指定下标开始切分数组,(List(1,2),List(3,4))
//前一个操作与下面这条语句相同
(nums.take(2),nums.drop(2))//(List(1,2),List(3,4))
//zip操作,返回的是List类型的元组
val numZ = List(1,2,3,4)
val chars = List('1','2','3','4')
numz zip chars //List((1,1),(2,2),(3,3),(4,4))
val name = List(a,b,c)
val age = List(3,4,5)
name zip age //List((a,3),(b,4),(c,5))
//List toString方法
numZ.toString
//List mkString方法
numZ.mkString
//转换成数组
nums.toArray//arr33 : Array[Int] = Array(1,2,3,4,5)
- 可变的序列import scala.collection.mutable.ListBuffer
//构建一个可变的list序列
val lst0 = ListBuffer[Int](1,2,3)
//创建一个空的可变列表
val lst1 = new ListBuffer[Int]
//向lst1中追加元素,注意,没有生成新的集合
lst1 +=4
lst1.append(5)
//将lst1中的元素追加到lst0中,注意,没有生成新的集合
lst0 ++= lst1
//将lst0和lst1合并成一个新的ListBuffer,注意,生成了一个集合
val lst2 = lst0 ++ lst1
//将元素追加到lst0的后面生成一个新的集合
val lst3 = lst0 :+ 5
8.4 set
- 不可变的set,import scala.collection.immutable.HashSet
val set1 = new HashSet[Int]()
//将元素和set1合并生成一个新的set,原有set不变
val set2 = set1 + 4
//set中元素不能重复,如果追加重复元素会进行去重
val set3 = set1 ++ Set(5,6,7)
val set0 = Set(1,3,4) ++ set1
println(set0.getClass)
- 可变的set,import scala.collection.mutable
//创建一个可变的set
val set1 = new mutable.HashSet[Int]()
//向HashSet中添加元素
set1 += 2
//add等价于+=
set1.add(4)
set ++= Set(1,3,5)
println(set1)
//删除一个元素
set1 -= 5
set1.remove(2)
println(set1)
- 求集合的交集并集差集
val set1 = Set(1,2,3,4,5)
val set2 = Set(3,4,5,6,7)
set1 union set2//相当于set1加set2,得到Set(5,1,6,2,7,3,4)
set1 diff set2//相当于set1减set2,得到Set(1,2)
set1 intersect set2//Set(5,3,4)
8.5 集合map
val map1 = new mutable.HashMap[String,Int]()
//向map中添加数据
map1("flink") = 1
map1 += (("hadoop",2))
map1.put("storm",3)
println(map1)
//从map中移除元素
map1 -= "hadoop"
map1.remove("storm")
println(map1)
8.6 元组tuple
- 映射是kv对偶集合,对偶是元组的最简单形式,元组可以装着多个不同类型的值
- 创建元组:
//定义元组时,用小括号将多个元素包起来,元素之间用逗号分隔,元素的类型可以不一样,元素的个数可以任意多个
val t = ("hadoop",3.14,65535)
val a = 1
val b = "name"
val c = true
val d = (a,b,c)//(Int,String,Boolean) = (1,"name",true)
val (a,b,c) = ("huangbo",18,true)//a="huangbo" b=18 c=true
- 获取元组中的值
val t,(a,b,c) = ("hadoop",3.14,65535) //t:(String,Double,Int) = ("hadoop",3.14,65535)
val r1 = t._1 //"hadoop"
val r2 = t._2 //3.14
//引用2.中的例子
val (a,b,c),t = ("huangbo",18,true)
t//(String,Int,Boolean) = ("huangbo",18,true)
t._1//String = "huangbo"
t._2//Int = 18
t._3//Boolean = true
//不能修改值
t._3=false//报错,不可以这么修改
- 将对偶的元组转成集合
//其实对于二元组集合,完全可以认为是一个map集合,所以二元组集合可以和map集合进行相互转换。
val arr = Array(("tom",88),("Jerry",95))//Map(tom -> 88,Jerry -> 95)
arr.toMap//toMap可以将对偶的集合转换成映射
- 元组拉链操作
//zip命令可以将多个值绑定在一起
val names = Array("tom","jerry","kitty")
val scores = Array(88,95,80)
names.zip(scores)
names zip scores
names.zip(scores).toMap
注意:如果两个数组的元素个数不一致,拉链操作后生成的数组的长度为较小的那个数组的元素的个数,相反操作unzip
8.7映射map
- 在scala中,把哈希表这种数据结构叫做映射,在java中也叫做映射。
- 在Python中,把哈希表这种数据结构叫做字典。
- 不管叫什么,存储的都是key-value这种键值对。
构建map:两种方式
val scores1 = Map("huangbo" -> 85,"xuzheng" -> 99,"huanglei" -> 90)
val scores2 = Map(("huangbo",85),("xuzheng",99),("huanglei",90))
获取和修改map当中的值
scores1("huangbo")//85
scores1.getOrElse("huangbo",100)//85,("huangbo",100)中的100为默认值,如果huangbo存在就获取,如果不存在就返回默认值。
scores1.getOrElse("huangbo11",100)//100,这里返回默认值100
注意:通常我们在创建一个集合时会用val这个关键字修饰一个变量(相当于java中的final),那么就意味着该变量的引用不可变,该引用中的内容是不是可变,取决于这个引用指向的集合中的类型。
常见的map操作:
val studentInfo = Map("john" -> 21,"stephen" -> 22,"lucy" -> 20)
studentInfo.clear()//不可清除,报错
val studentInfoMutable = scala.collention.mutable.Map("john" -> 21,"stephen" -> 22,"lucy" -> 20)
//清空map
studentInfoMutable.clear()
studentInfoMutable//scala.collention.mutable.Map[String,Int] = Map()
//遍历map
for(i <- studentInfoMutable) println(i)//空
val studentInfoMutable = scala.collention.mutable.Map("john" -> 21,"stephen" -> 22,"lucy" -> 20)
for(i <- studentInfoMutable) println(i)//(john,21) (lucy,20) (stephen,22)
studentInfoMutable.foreach(e => println(e._1+":"+e._2))//john:21 lucy:20 stephen:22
//put操作
val xMap = new scala.collection.mutable.HashMap[String,Int]()
xMap.put("flink",1)//Option[Int] = None
xMap.put("flink",1)//Some(1)
xMap.contains("flink")//true
val xMap = scala.collection.mutable.Map(("flink",1),("hive",1))
"flink" -> 1//(String,Int) = ("flink",1)
xMap.get("flink")//Option[Int] = Some(1)
xMap.get("SparkSql")//Option[Int] = None
8.8 Option None Some类型
Option None Some是scala中定义的类型,他们在scala中十分常用。
None Some是Option的子类,他主要解决值为null的问题,在java语言中,对于定义好的HashMap,如果get方法中传入的键不存在,方法会返回null,在编写代码的时候对于null这种情况要特殊处理,然而在实际中经常会忘记,因此他很容易引起空指针异常。
在scala语言中通过Option None Some这三个类来避免这样的问题,这样做有几个好处,首先是代码可读性更强,当看到Option时,我们自然而然知道他的值就是可选的,然后变量是Option,比如Option[String]的时候,直接使用String的话,编译直接通不过。
val m1 = scala.collection.mutable.Map(("a",1))
m1.get("a")//Option[Int] = Some(1)
m1.get("aa")//Option[Int] = None
总结一下常见的scala中的“空”值:Nothing,None,Nil,Null,Unit
nothing:表示所有类的子类,没有任何意义。
none:从map中使用get方法获取值,如果没有获取到就是none
Nil:表示一个空序列
Null:所有引用类型的子类。
Unit:抽象了void这个关键字,表示一个方法没有返回值的抽象。