Scala 基础
1.课程目标
目标1:熟练使用scala编写Spark程序
目标2:动手编写一个简易版的Spark通信框架
目标3:为阅读Spark内核源码做准备
2.Scala的基本介绍
scala官方网址:
http://www.scala-lang.org
当前版本2.13.3.使用最多的版本2.11.8
2.10.x系统的版本不兼容2.11.8
2.1 什么是Scala
Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程(OOP)和函数式编程(FP)的各种特性。Scala运行于Java平台(JVM)最终需要编译成字节码文件,并兼容现有的Java程序。
2.2 为什么要学Scala
1、优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。
2、速度快:Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以和JRuby,Groovy比起来速度会快很多。
3、能融合到Hadoop生态圈:Hadoop现在是大数据事实标准,Spark并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。
3.Scala编译器安装
3.1 安装JDK
因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK。
3.2 安装Scala
Windows安装Scala编译器
访问Scala官网http://www.scala-lang.org/下载Scala编译器安装包,目前最新版本是2.12.x,
这里下载scala-2.11.8.msi后点击下一步就可以了(自动配置上环境变量)。也可以下载scala-2.11.8.zip,
解压后配置上环境变量就可以了。
Linux安装Scala编译器
下载Scala地址https://www.scala-lang.org/download/2.11.8.html
然后解压Scala到指定目录
tar -zxvf scala-2.11.8.tgz -C /usr/java
配置环境变量,将scala加入到PATH中
vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.8 (已经配置过不需要重复配置)
## SCALA_HOME
export SCALA_HOME=/export/servers/scala-2.11.8
export PATH=PATH:SCALA_HOME/bin
3.3 安装IDEA中Scala插件
Scala开发工具安装(IDEA离线)
由于IDEA的Scala插件更优秀,大多数Scala程序员都选择IDEA,可以到http://www.jetbrains.com/idea/download/下载,点击下一步安装即可,安装时如果有网络可以选择在线安装Scala插件。
这里我们使用离线安装Scala插件:
1.安装IDEA,点击下一步即可。
2.下载IEDA的scala插件
插件地址: https://plugins.jetbrains.com/plugin/1347-scala
3.安装Scala插件:Configure -> Plugins -> Install plugin from disk -> 选择Scala插件 -> OK -> 重启IDEA
Scala开发工具安装(IDEA在线)
安装Scala插件:Configure -> Plugins -> 搜索Scala -> 在线安装 -> 重启IDEA
3.4 scala的REPL
REPL ==> 交互式解析器环境
R(read)、E(evaluate) 、P(print)、L(loop)
交互式的环境,所见即所得
使用:help查看帮助文档
3.5 创建工程支持scala代码开发
第一步:idea当中创建创建普通maven工程
第二步:修改pom.xml导入jar包
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
<!-- 如果想要用java -jar 来运行我们打包之后的jar包,则下面这个配置必须注释掉 -->
<!-- <scope>provided</scope>-->
</dependency>
</dependencies>
<build>
<plugins>
<!-- 限制jdk版本插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 编译scala需要用到的插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 项目打包用到的插件
将我们一些依赖的jar包,全部都打包到一起去
-->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.itcast.scala.demo1.ScalaFirst</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
第三步:创建scala的资源文件夹
第四步:开发scala的程序
在java当中,静态的属性或者方法都是可以直接调用
在scala当中,为了解决静态和非静态的区分,专门有一个对象叫做Object,Object里面所有的东西都是类似于java当中的静态的static修饰的
第五步:打包运行scala的程序
更改类的相对路径
四种运行方式都可以用于运行我们的jar包
第一种运行方式:我们打包的时候指定了对应的main方法所在的程序类
scala scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第二种运行方式:不管打包的时候有没有指定main方法所在的程序类,都可以运行
scala -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
第三种方式:我们打包的时候指定了对应的main方法所在的程序类
java -jar scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第四种方式:不管打包的时候有没有指定main方法所在的程序类,都可以运行
java -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
4.Scala基础
4.1 scala当中申明值和变量
scala当中的变量申明可以使用两种方式,第一种使用val来申明常量。第二种使用var来申明变量。
申明变量语法
val/var 变量名 [:变量类型] = 变量值
4.2 块表达式
定义变量时用 {} 包含一系列表达式,其中块的最后一个表达式的值就是块的值。
var hello = {
println("world")
val d = 20
val c = 10
d-c
}
块表达式最后的结果,就是我们变量的值
4.3 scala当中常用数据类型
Scala和Java一样,有7种数值类型Byte、Char、Short、Int、Long、Float、Double类型和1个Boolean类型。大小写敏感
scala当中的String,其实就是使用了java当中String,但是scala当中的string的方法更加丰富,scala当中给String扩展了很多方法,但是没有改String的源码,使用隐式转换来丰富了很多的方法
与java当中不同,scala当中并不区分基本数据类型和引用数据类型,所有的这些类型全部都是对象
每一种数据类型都有对应的Rich* 类型,如RichInt、RichChar等,为基本类型提供了更多的有用操作。
4.4 scala当中的常用类型结构图
值类型与引用类型
值类型:一些数据值
引用类型:我们自己定义的一些scala的class类,一些java类等等
null是所有引用类型的子类
nothing是所有引用类型和值类型的子类
4.5 算数操作符重载
scala当中也有 + – * / 的操作,但是 + – * / 都是方法名
方式1:空格 直接写方法名
方式2:.方法名(方法参数)
5.scala当中流程控制语句
5.1 if判断
val a = 20
val aResult = if(a > 10){
10
}else{
30
}
println(aResult)
val b = 20
val bResult = if(b > 10){
"hello"
}else{
30
}
println(aResult)
5.2 whie循环
//while循环没有任何返回值类型,关注的是执行的过程
var e = 1
val f = while(e <= 10){
e += 1
}
println(f)
//循环终止
var g = 10
val loop = new Breaks
loop.breakable{
val h = while(g <=20){
g +=1
println(g)
if(g == 15){
loop.break()
}
}
println(h)
}
println(g+"=============")
5.3 for循环
scala当中,为for循环这一常用的循环结构提供了很多的特性,这些特性被称之为for推导式或者for表达式
示例一:使用to实现左右两边均为闭合的访问
for(i <- 1 to 3; j <- 1 to 5){
println( i *j +" result result result result")
}
示例二:使用util实现左右两边分别为前闭后开的访问
for(i <- 1 until 5 ;j <- 2 until 5){
println(i * j )
}
示例三:引入保护式(也称条件判断式)。我们可以在for循环条件里面加入判断表达式,如果满足则进入for循环,如果不满足则不进入for循环。类似于java当中的continue的功能类似
for(i <- 1 to 5 if i!=2){
println(i)
}
示例四:引入变量
for(i <- 1 to 3 ;j = 4-i){
println(i * j )
}
示例五:将遍历过程中处理的结果返回到一个变量,使用yield关键字进行接收
val for5 = for(i <- 1 to 10) yield i
println(for5+"for5")
示例六:使用大括号代替小括号
for{
i <- 1 to 5
j = 5-i
}
println( i* j +"myij")
6.scala当中的函数与方法
在scala当中,函数与方法是两个不同的概念,函数是scala当中的一等公民,scala是一门函数式的编程语言,同时兼顾了面向对象语言的特性
6.1 调用函数与方法
scala是一个面向函数式的编程语言,同时兼容面向对象的编程的特性
-
调用函数,求方根
scala> import scala.math._ scala> sqrt(100)
-
调用方法,静态方法(scala中没有静态方法这个概念,需要通过伴生类对象来实现)
生成一个随机的素数scala> BigInt.probablePrime(16, scala.util.Random)
-
调用方法,非静态方法,使用对象调用
scala> "HelloWorld".distinct
-
apply与update方法
apply方法是调用时可以省略方法名的方法。用于构造和获取元素:"Hello"(4) 等同于 "Hello".apply(4) Array(1,2,3) 等同于 Array.apply(1,2,3) 如: println("Hello"(4)) println("Hello".apply(4))
在StringOps中你会发现一个 def apply(n: Int): Char方法定义。update方法也是调用时可以省略方法名的方法,用于元素的更新:
arr(4) = 5 等同于 arr.update(4,5)
如:
val arr1 = new Array[Int](5)
arr1(1) = 2
arr1.update(1, 4)
println(arr1.mkString(","))
6.2 scala方法的定义(重点)
方法与函数的定义形式不一样。方法就是函数,函数也是对象
方法定义的形式:
def 方法名(参数1:参数类型1,参数2:参数类型2):返回值的类型 = {
方法体
}
需求1:定义一个方法,方法名是hello,两个参数,一个参数int类型,一个参数是string,返回值int类相关
def hello (frist:Int , second:String):Int = {
30
first
}
需求2:定义一个方法,且不定义返回值
def hello2(first:Int , second:String) ={
//println(first)
//20
}
val hello2Result = hello2(20,"abc")
println( hello2Result)
注意:如果定义的方法没有返回值,那么方法的返回值会做自动推断。根据我们方法的最后一个返回类型来推断我们的方法返回类型
需求3:定义一个方法,不定义返回值,可以通过自动推断,返回不同类型的值
def hello3(first:Int,second:String) ={
if(first > 10){
first
}else{
second
}
}
val hello3Result = hello3(5,"helloworld")
or
val hello3Result = hello3(second = "helloworld",first = 55)
println(hello3Result)
需求4:定义一个方法,参数给定默认值,如果不传入参数,就使用默认值来代替
def hello4(first:Int = 10,second:String)={
println(first+"/t"+ second)
}
//注意我们在调用方法的时候我们可以通过参数名来指定我们的参数的值
hello4(second="helloworld")
需求5:变长参数,方法的参数个数不定的,类似于java当中的方法的…可变参数
def hello5(first:Int*)={
var result = 0;
for(arg <- first){
result += arg
}
println(result)
}
hello5(10,20,30)
hello5(10,50)
需求6:递归函数。我们可以定义一个方法,使得方法自己调用自己,形成一个递归函数,但是方法的返回值类型必须显示的手动指定
def hello6(first:Int):Int={
if(first <= 1){
1
}else{
first * hello6(first -1)
}
}
val hello6Result = hello6(10)
println(hello6Result)
需求7:定义一个方法,没有显示的指定返回值,那么我们方法当中定义的等号可以省掉
注意:如果省掉了=号,那么这个方法强调的就是一个代码执行的过程
/**
* 定义了一个方法,但是方法的返回值没有显示指定,
* 此时我们就可以省掉方法定义的=号,如果省掉 = 号,
* 那么这个方法强调的是一个过程,代码执行的过程,
* 不会产生任何的返回值
* @param first
*/
def hello7(first:Int){
println(first)
30
}
hello7(20)
需求8:直接通过def定义一个方法
def hello8() {"abc"}
def hello8new() "abc"
def hello8new2 "abc"
def hello8new2 = "abc"
需求9:如果方法体当中只有一行代码,我们也可以省掉大括号
def hello10(first:Int,second:Int) = first+second
val hello10Result = hello10(10,20)
println(hello10Result)
6.3 scala函数的定义
关键字 =>
简单的认为,看到def就是方法,看到 => 就是一个函数
函数定义的两种形式
第一种形式:
val 函数名 = (参数名1:参数类型1,参数名2:参数类型2) => {函数体}
需求1:定义一个标准函数,使用 =>来进行定义
val func1 =(x:Int,y:Int) =>{
x+y
}
func1(2,8)
需求2:定义匿名函数。也就是我们可以定义一个没有名字的函数
定义一个匿名函数之后,这个函数就没法使用了
(x:Int,y:String) =>{x + y}
第二种形式:定义函数的大的框架
val 函数名 :(参数类型1,参数类型2) => (返回类型) = {
函数体
}
需求3:定义一个函数,参数值是两个,分别是Int和String,返回值是一个元组,分别是String和Int
val func4:(Int,String) =>(String,Int) ={
(x,y) => (y,x)
/*
相当于匿名函数 (x:Int,y:String) => x + y
*/
}
val func4Result = func4(10,"hello")
println(func4Result)
6.4 scala当中函数与方法的区别以及方法转换成函数
方法就是函数
函数也是对象
函数可以作为一个参数,传入到方法里面去叫做高阶函数
示例:
val myfunc=(参数名1:参数类型1) =>{
}
方法就是函数,函数也是一个对象(first是Int类型的对象,myfunc也是对象)
def hello10(first:Int,myfunc):Int{}
示例1:
val myFunc = (x:Int) =>{
x * x
}
val myFunc2 :(Int) => Int ={
x => x * x
}
def methodFunction(f:Int => Int):Int ={
println(f(100))
f(100)
}
val methodFunctionResult = methodFunction(myFunc)
val methodFunctionResult2 = methodFunction(myFunc2)
println(methodFunctionResult)
println(methodFunctionResult2)
方法可以自动转换成函数作为参数传递到方法里面去
def method2(x:Int) ={ x * x }
def methodFunc2(x:Int => Int):Int ={
x(100)
}
val methodFunc2Result = methodFunc2(method2)
println(methodFunc2Result)
可以通过隐式转换将方法变成函数
def method3(x:Int,y:String) = {
x
}
println(method3 _)
6.5 懒值加载
当val被声明为lazy时,他的初始化将被推迟,等到调用时再分配内存空间
def init(): String = {
println("init方法执行")
"嘿嘿嘿,喵喵喵~"
}
lazy val msg = init()
println("lazy方法没有执行")
println(msg)
7.Scala数据结构
7.1 数据结构特点
Scala同时支持可变集合和不可变集合,不可变集合从不可变,可以安全的并发访问。
两个主要的包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
Scala优先采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本。
不可变集合继承层次:
可变集合继承层次:
7.2 数组
1、定义定长数组
我们可以定义一个固定长度大小和类型的定长数组
java中:
Integer[] i = new Integer[20]
scala中:
//定义一个数组长度为10,类型为Int的固定大小数组
val array = new Array[Int](10)
array(1) = 10
array(2) = 20
//访问数组当中的某一个下标元素值
println(array(1))
//直接使用apply伴生类进行生成一个数组
val array2 = Array(1,2,3)
//访问数组的元素
println(array2(2))
2、变长数组
我们也可以通过ArrayBuffer来定义一个变长数组
val array3 = new ArrayBuffer[Int]()
array3.append(10,20,30,40)
priintln(array3.mkString(",")) 将地址值转变为字符类型并通过","隔开
//伴生类
val array4 = ArrayBuffer[String]()
array4.append("helloworld")
3、定长数组与变长数组的相互转换
定长数组转换成变长数组
//定长数组转换成变长数组
val toBuffer: mutable.Buffer[Int] = array.toBuffer
toBuffer.append(50)
变长数组转换为定长数组
//变长数组准换成定长数组
val toArray: Array[Int] = array3.toArray
4、多维数组
我们可以通过Array的ofDim方法来定义一个多维的数组,多少行,多少列,都是我们自己定义说了算
val dim = Array.ofDim[Double](3,4)
dim(1)(1) = 11.11
println(dim.mkString(","))
5、scala当中数组的遍历
val array5 = ArrayBuffer(1,2,3,4,5,6)
for(x <- array5){
println(x )
}
6、数组的常见算法
val array6 = Array(1,2,3,4,5,6)
//求和
array6.sum
//求最大值
array6.max
//求最小值
array6.min
//排序
array6.sorted
7.3 scala中元组tuple
在scala当中提供元组tuple的数据类型,可以理解tuple为一个容器,可以存放各种不同的数据类型的数据,例如一个Tuple当中既可以存放String类型数据,同时也可以存放Int类型的数据
注意:注意元组一旦创建之后,就是不可变的,也就是说元组当中没有添加和删除元素这一说
1、创建元组
创建元组,直接使用小括号,小括号当中存放我们元组当中各种类型的元素即可
val tuple1 = ("hello",1,5.0f)
println(tuple1)
2、元组数据的访问
访问元组当中的数据直接使用_加角标即可,但是要注意,元组当中的数据角标是从1开始的
val tuple1 = ("hello",1,5.0f)
println(tuple1)
val tuple1Result = tuple1._1
println(tuple1Result)
3、元组的遍历
//第一种方式遍历元组
for(x <- tuple1.productIterator){
println(x)
}
//第二种方式遍历元组
tuple1.productIterator.foreach( x => println(x))
7.4.映射Map
scala当中的Map集合与java当中的Map类似,也是key,value对形式的
1、不可变映射
val map1 = Map("hello" ->"world","name" -> "zhangsan","age" -> 18)
2、可变映射及其操作
val map2 = scala.collection.mutable.Map("hello" ->"world","name" -> "zhangsan","age" -> 18)
//可变map添加元素
map2.+=("address" ->"地球")
println(map2)
//可变map删除元素.注意,删除元素是返回一个删除元素之后的map,原来的map并没有改变
val map3 = map2.-("address")
println(map2)
println(map3)
//或者使用覆盖key的方式来更细元素
map2 += ("address" -> "北京")
println(map2)
//或者使用 + 来进行更新元素
map2 +("address" ->"上海","phonNo" -> "13688886666")
println(map2)
"+"跟"+="没啥区别
3、获取map当中指定的key值
//通过key来进行取值
map2.get("address") //如果有则为some类型与值,没有则返回none
//通过key来进行取值,如果没有这个key,就用后面给定的默认值
map2.getOrElse("address","非洲")
//通过key来进行取值,如果有则用已经有的值,没有那么就用后面给定的默认值
map2.getOrElse("phoNo","13133335555")
4、遍历Map当中的元素
//遍历key与value
for((k,v) <- map2){
println(k)
println(v)
}
//遍历获取所有的key
for(k <- map2.keys) {
println(k)
}
//遍历获取所有的value
for(v <- map2.values) {
println(v)
}
//打印key,value对
for(kv <- map2){
println(kv)
}
5、将对偶的数组转变为map
//将对偶的元组转变为map
val arrayMap = Array(("name","zhangsan"),("age",28))
val toMap: Map[String, Any] = arrayMap.toMap
println(toMap)
7.5 列表(List)
scala当中也提供有与java类似的List集合操作
1、创建列表
注意:列表当中的元素类型可以是不同的,这一点与我们元组类似,但是列表当中的元素是可以删减的
val list1 = List("hello",20,5.0f)
println(list1)
2、访问列表当中的元素
//访问列表当中的元素
val list1Result = list1(0)
println(list1Result)
3、列表当中添加元素
我们可以从列表头部或者尾部添加元素
val list2 = list1:+50 //尾部添加
val list3 = 100+:list1 //头部添加
println(list2)
println(list3)
4、List的创建与追加元素
Nil是一个空的List,定义为List[Nothing]
//尾部添加了Nil,那么就会出现List集合里面装List集合的现象
val list4 = 1::2 ::3 :: list1 ::Nil
println(list4)
//尾部没有添加Nil的值,那么所有的元素都压平到一个集合里面去了
val list5 = 1::2::3::list1
println(list5)
5、变长List的创建与使用
val list6 = new ListBuffer[String]
list6.append("hello")
list6.append("world")
println(list6.mkString(","))
//转成不可变列表
val list7 = list6.toList
println(list7)
6、List操作延伸阅读
Scala是函数式风格与面向对象共存的编程语言,方法不应该有副作用是函数风格编程的一个重要的理念。方法唯一的
效果应该是计算并返回值,用这种方式工作的好处就是方法之间很少纠缠在一起,因此就更加可靠和可重用。另一个
好处(静态类型语言)是传入传出方法的所有东西都被类型检查器检查,因此逻辑错误会更有可能把自己表现为类型错误。把这个函数式编程的哲学应用到对象世界里以为着使对象不可变。
前面一章介绍的Array数组是一个所有对象都共享相同类型的可变序列。比方说Array[String]仅包含String。
尽管实例化之后你无法改变Array的长度。因此,Array是可变的对象。
说到共享相同类型的不可变对象类型,Scala的List类才是。和数组一样,List[String]包含的仅仅是String。
Scala的List不同于Java的java.util.List,总是不可变的(Java的List是可变)。更准确的说法,Scala的
List是设计给函数式风格的编程用的。
(1)List类型定义以及List的特点:
//字符串类型List
scala> val fruit=List("Apple","Banana","Orange")
fruit: List[String] = List(Apple, Banana, Orange)
//前一个语句与下面语句等同
scala> val fruit=List.apply("Apple","Banana","Orange")
fruit: List[String] = List(Apple, Banana, Orange)
//数值类型List
scala> val nums=List(1,2,3,4,5)
nums: List[Int] = List(1, 2, 3, 4, 5)
//多重List,List的子元素为List
scala> val list = List(List(1, 2, 3), List("adfa", "asdfa", "asdf"))
list: List[List[Any]] = List(List(1, 2, 3), List(adfa, asdfa, asdf))
//遍历List
scala> for(i <- list; from=i; j<-from)println(j)
1
2
3
adfa
asdfa
asdf
(2)List与Array的区别:
1、List一旦创建,已有元素的值不能改变,可以使用添加元素或删除元素生成一个新的集合返回。
如前面的nums,改变其值的话,编译器就会报错。而Array就可以成功
scala>nums(3)=4
<console>:10: error: value update is not a member of List[Int]
nums(3)=4
^
2、List具有递归结构(Recursive Structure),例如链表结构
List类型和气他类型集合一样,它具有协变性(Covariant),即对于类型S和T,如果S是T的子类型,则List[S]也是
List[T]的子类型。
例如:
scala>var listStr:List[Object] = List("This", "Is", "Covariant", "Example")
listStr:List[Object] = List(This, Is, Covariant, Example)
//空的List,其类行为Nothing,Nothing在Scala的继承层次中的最底层
//,即Nothing是任何Scala其它类型如String,Object等的子类
scala> var listStr = List()
listStr:List[Nothing] = List()
scala>var listStr:List[String] = List()
listStr:List[String] = List()
(3)List常用构造方法
//1、常用::及Nil进行列表构建
scala> val nums = 1 :: (2:: (3:: (4 :: Nil)))
nums: List[Int] = List(1, 2, 3, 4)
//由于::操作符的优先级是从右向左的,因此上一条语句等同于下面这条语句
scala> val nums = 1::2::3::4::Nil
nums:List[Int] = List(1, 2, 3, 4)
至于::操作符的使用将在下面介绍
(4)List常用操作
//判断是否为空
scala> nums.isEmpty
res5: Boolean = false
//取第一个元素
scala> nums.head
res6: Int = 1
//取列表第二个元素
scala>nums.tail.head
res7: Int = 2
//取第三个元素
scala>nums.tail.tail.head
res8: Int = 3
//插入操作
//在第二个位置插入一个元素
scala>nums.head::(3::nums.tail)
res11: List[Int] = List(1, 3, 2, 3, 4)
scala> nums.head::(nums.tail.head::(4::nums.tail.tail))
res12: List[Int] = List(1, 2, 4, 3, 4)
//插入排序算法实现
def isort(xs: List[Int]):List[Int] = {
if(xs.isEmpty) Nil
else insert(xs.head, issort(xs.tail))
}
def insert(x:Int, xs:List[Int]):List[Int] = {
if(xs.isEmpty || x <= xs.head) x::xs
else xs.head :: insert(x, xs.tail)
}
//连接操作
scala>List(1, 2, 3):::List(4, 5, 6)
res13: List[Int] = List(1, 2, 3, 4, 5, 6)
//去除最后一个元素外的元素,返回的是列表
scala> nums.init
res13: List[Int] = List(1, 2, 3)
//取出列表最后一个元素
scala>nums.last
res14: Int = 4
//列表元素倒置
scala> nums.reverse
res15: List[Int] = List(4, 3, 2, 1)
//一些好玩的方法调用
scala> nums.reverse.reverse == nums
//丢弃前面n个元素
scala>nums drop 3
res16: List[Int] = List(4)
//获取前面n个元素
scala>nums take 1
res17: List[Int] = List[1]
//将列表进行分割
scala> nums.splitAt(2)
res18: (List[Int], List[Int]) = (List(1, 2),List(3, 4))
//前一个操作与下列语句等同
scala> (nums.take(2),nums.drop(2))
res19: (List[Int], List[Int]) = (List(1, 2),List(3, 4))
//Zip操作
scala> val nums=List(1,2,3,4)
nums: List[Int] = List(1, 2, 3, 4)
scala> val chars=List('1','2','3','4')
chars: List[Char] = List(1, 2, 3, 4)
//返回的是List类型的元组(Tuple),返回的元素个数与最小的List集合的元素个数一样
scala> nums zip chars
res20: List[(Int, Char)] = List((1,1), (2,2), (3,3), (4,4))
//List toString方法
scala> nums.toString
res21: String = List(1, 2, 3, 4)
//List mkString方法
scala> nums.mkString
res22: String = 1234
//转换成数组
scala> nums.toArray
res23: Array[Int] = Array(1, 2, 3, 4)
(5)List伴生对象方法
//apply方法
scala> List.apply(1, 2, 3)
res24: List[Int] = List(1, 2, 3)
//range方法,构建某一值范围内的List
scala> List.range(2, 6)
res25: List[Int] = List(2, 3, 4, 5)
//步长为2
scala> List.range(2, 6,2)
res26: List[Int] = List(2, 4)
//步长为-1
scala> List.range(2, 6,-1)
res27: List[Int] = List()
scala> List.range(6,2 ,-1)
res28: List[Int] = List(6, 5, 4, 3)
//构建相同元素的List
scala> List.make(5, "hey")
res29: List[String] = List(hey, hey, hey, hey, hey)
//unzip方法
scala> List.unzip(res20)
res30: (List[Int], List[Char]) = (List(1, 2, 3, 4),List(1, 2, 3, 4))
//list.flatten,将列表平滑成第一个无素
scala> val xss =
| List(List('a', 'b'), List('c'), List('d', 'e'))
xss: List[List[Char]] = List(List(a, b), List(c), List(d, e))
scala> xss.flatten
res31: List[Char] = List(a, b, c, d, e)
//列表连接
scala> List.concat(List('a', 'b'), List('c'))
res32: List[Char] = List(a
, b, c)
(6)::和:::操作符介绍
List中常用'::',发音为"cons"。Cons把一个新元素组合到已有元素的最前端,然后返回结果List。
scala> val twoThree = List(2, 3)
scala> val oneTwoThree = 1 :: twoThree
scala> oneTwoThree
oneTwoThree: List[Int] = List(1, 2, 3)
上面表达式"1::twoThree"中,::是右操作数,列表twoThree的方法。可能会有疑惑。表达式怎么是右边参数的方法
,这是Scala语言的一个例外的情况:如果一个方法操作符标注,如a * b,那么方法被左操作数调用,
就像a.* (b)--除非方法名以冒号结尾。这种情况下,方法被右操作数调用。
List有个方法叫":::",用于实现叠加两个列表。
scala> val one = List('A', 'B')
val one = List('A', 'B')
scala> val two = List('C', 'D')
scala> one:::two
res1: List[Char] = List(A, B, C, D)
List与Array的区别:
1、List一旦创建,已有元素的值不能改变,可以使用添加元素或删除元素生成一个新的集合返回。
2、List具有递归结构(Recursive Structure),例如链表结构
List类型和其他类型集合一样,它具有协变性(Covariant),即对于类型S和T,如果S是T的子类型,则List[S]也是List[T]的子类型。
7.6 Set集合
集是不重复元素的结合。集不保留顺序,默认是以哈希集实现。
如果想要按照已排序的顺序来访问集中的元素,可以使用SortedSet(已排序数据集),已排序的数据集是用红黑树实现的。
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。
1、不可变集合的创建
val set1 =Set("1","1","2","3")
println(set1.mkString(","))
2、可变集合的创建以及添加元素
如果我们引入的集合的包是可变的,那么我们创建的集合就是可变的
import scala.collection.mutable.Set
val set2 = Set(1, 2, 3)
set2.add(4)
set2.+=(10)
set2 += 5
//使用.这个方法添加元素,会返回一个新的集合
val set3 = set2.+(6)
println(set2.mkString(","))
println(set3.mkString("/001"))
3、可变集合删除元素
set3 -= 1
println(set3.mkString("."))
set3.remove(2)
println(set3.mkString("."))
4、遍历Set集合元素
for(x <- set3){
println(x )
}
注意:如果要创建有序的set,那么需要使用SortedSet。用法与Set类似
5、Set更多常用操作介绍
7.7 集合元素与函数的映射
我们可以使用map方法,传入一个函数,然后将这个函数作用在集合当中的每一个元素上面
map:将集合中的每一个元素映射到某一个函数,返回一个新的集合
val listFunc = List("name","age","zhangsan","lisi")
println(listFunc.map(x => x +"hello"))
println(listFunc.map(x => x.toUpperCase())) **如果=>在对象的右边只调用用了一次,那么可以用_代替
||
println(listFunc.map(_.toUpperCase()))
flatmap:flat即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合
val listFunc2 = List("address","phonNo")
val flatten = listFunc2.flatten
println(listFunc2.flatMap( x => x +"WORLD")) 先操作后压平
||
println(listFunc2.flatMap( _ +"WORLD"))
7.8 队列Queue
队列Queue是一个先进先出的结构
1、创建队列
//创建可变的队列
import scala.collection.mutable
val queue1 = new mutable.Queue[Int]()
println(queue1)
2、队列当中添加元素
//队列当中添加元素
queue1 += 1
//队列当中添加List
queue1 ++=List(2,3,4)
println(queue1)
3、按照进入队列顺序,删除队列当中的元素(弹出队列)
val dequeue = queue1.dequeue()
println(dequeue)
println(queue1)
4、向队列当中加入元素(入队列操作)
//塞入元素到队列
queue1.enqueue(5,6,7)
println(queue1)
5、获取第一个与最后一个元素
//获取第一个元素
println(queue1.head)
//获取最后一个元素
println(queue1.last)
7.9 集合当中的化简、折叠与扫描操作
1、折叠、化简 reduce操作
将二元函数引用集合当中的函数
val reduceList = List(1,2,3,4,5)
//1-2-3-4-5 = -13
val reduceLeftList = reduceList.reduceLeft(_ - _) # 跟map reduce中reduce一样实现聚合效果
val reduceLeftList2 = reduceList.reduceLeft((x,y) => x-y)
println(reduceLeftList)
println(reduceLeftList2)
//reduceRight操作
// 4-5 = -1
// 3- (-1) = 4
//2-4 = -2
//1 -(-2) = 3
val reduceRightList = reduceList.reduceRight(_ - _)
println(reduceRightList)
2、折叠、化简folder操作
fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。可以把reduceLeft看做简化版的foldLeft。
val foldList = List(1,9,2,8)
val foldResult = foldList.fold(10)((x,y) => x+y)
println(foldResult)
foldLeft操作
//50-1-9-2-8 = 30
val foldLeftResult = foldList.foldLeft(50)((x,y) => x-y)
println(foldLeftResult)
7.10 拉链操作
对于多个List集合,我们可以使用Zip操作,将多个集合当中的值绑定到一起去
val zipList1 = List("name","age","sex")
val zipList2 = List("zhangsan",28)
val zip = zipList1.zip(zipList2)
||
zipList1 zip zipList2
val toMap1 = zip.toMap
println(zip)
println(toMap1)
7.11 迭代器
对于集合当中的元素,我们也可以使用迭代器来进行遍历
val listIterator = List(1,2,"zhangsan")
val iterator = listIterator.iterator
while(iterator.hasNext){
println(iterator.next())
}
7.12 线程安全的集合
scala当中为了解决多线程并发的问题,提供对应的线程安全的集合
https://www.scala-lang.org/api/2.11.8/#package
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
7.13 操作符
大致了解即可
1) 如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号:
val `val` = 42
2) 这种形式叫中置操作符,A 操作符 B 等同于 A.操作符(B)
3) 后置操作符,A操作符等同于A.操作符,如果操作符定义的时候不带()则调用时不能加括号
4) 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符。
5) 赋值操作符,A操作符=B等同于A=A操作符B
拜师教育学员文章:作者:976-沈同学,
转载或复制请以 超链接形式 并注明出处 拜师资源博客。
原文地址:《24 Scala 基础》 发布于2020-07-16
评论 抢沙发