scala学习——基础知识

Scala是一门融合了面向对象和函数式编程的静态类型语言,运行在JVM上。它具有面向对象、函数式编程、静态类型、并发性、简洁语法等特点。学习Scala需要注意其与Java的类库差异,并且Scala中的变量分为var和val,数据类型包括Unit、Null、Nothing、Any等。此外,Scala支持数组、列表、元组、集合和Map等数据结构,以及Actor模型进行并发处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

 

scala 官网:https://www.scala-lang.org/

是 Scalable Language 的简写,是一门多范式的编程语言,联邦理工学院洛桑(EPFL)的Martin Odersky于2001年基于Funnel的工作开始设计Scala。Funnel是把函数式编程思想和Petri网相结合的一种编程语言。

Scala(Scalable Language)以一种简洁、高级的语言将面向对象函数式编程结合在一起.Scala的静态类型有助于避免复杂应用程序中的bug,它的JVM和JavaScript运行时允许您构建高性能的系统,可以轻松地访问庞大的库生态系统。

Scala是一门以Java虚拟机为目标运行环境并将面向对象和函数式编程语言的最佳特性结合在一起的编程语言。Scala是面向对象的,每一个值都是一个对象,对象的类型和行为由类定义,不同而的类可以通过混入(mixin)的方式结合在一起。Scala也支持一种通用形式的模式匹配,模式匹配用来操作代数式类型。Scala可以调用Java方法,创建Java对象,继承Java类和实现Java接口。

Scala的特点:

(1)面向对象: scala是一个纯面向对象的语言,所有的值都是对象、类和对象行为用类和特质来描述

(2)函数式编程(项目的组成是函数):每个函数都是一个值,原生支持嵌套函数定义和高阶函数。

(3)静态类型:Scala具备类型系统,通过编译时检查,保证代码的安全性和一致性

  • 类型在编译阶段确定–静态语言------->强类型语言:(类型转换时)必须强制类型转换,使用变量之前必须声明数据类型。
  • 类型在运行阶段确定–动态语言------->弱类型语言:自动类型转换,变量使用之前不需要类型声明.

(4)并发性:Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱发收消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现

(5)简洁优雅灵活的语法

(6)可扩展的框架:1) 使用trait实现的混合结构;2) 抽象类型成员和泛型;3) 嵌套类;4) 显式自类型self type)。

Java 和 scala 以及 JVM 的关系图

Scala 比较难学的原因主要是:

语法兼顾面向对象和函数式编程,比较容易混淆
部分支持 Java 的类库,又有自己的类库,还对 Java 的部分类库做了封装,查看 Scala 源码要求较高
由于是对 Java 的 “封装”,学习 Scala 的前提就是要会 Java?   (我的java学的超级差)

Scala 学习建议
主要就是学习 Scala 的特殊语法
区别 Scala 和 Java  规范的使用 Scala 编写代码

Scala的基本语法

Scala中的变量:Scala与Java有着相同的数据类型,下面列出一些Scala有的数据类型。

1.var:如同Java里的非 final 变量,在生命周期中可以被多次赋值。

2.val:类似于Java里的final变量。一旦被初始化,便不能再被赋值。

变量或者函数的类型需要卸载变量或者函数名称的后面。

Scala数据类型:

Scala与Java有着相同的数据类型,下面列出一些Scala有的数据类型。

  1. Unit:表示无值,和其他语言的void一样。
  2. Null:null或空引用。
  3. Nothing:是scala的类层级的最低端,是任何其他类型的子类型。
  4. Any:是所有其它类的超类
  5. AnyRef:是scala所有类的基类

Scala种类型有:Byte、Char、Short、Int、Long、Float、Double和一个Boolean类型。跟java不同的是这些类型是类。scala并不刻意区分基本类型和引用类型。(scala中都是对象,所以类型首字母要大写)

scala用底层的java.lang.String类来表示字符串,用 StringOps 类给字符串操作。还提供了RichInt、RichDouble、RichChar等为Int、Double、Char提供便捷,to方法也是RichInt方法中的。还有BIgInt和BigDecimal类,用于任意大小(但有穷)的数字,这些类背后分别对应的是java.math.BigInterger和java.math.BigDecimal。

BigInt(支持任意大小的整数,http://www.scala-lang.org/api/current/scala/math/BigInt.html

BigDecimal(支持任意大小的浮点数,http://www.scala-lang.org/api/current/scala/math/BigDecimal.html)。

Scala函数的定义

格式:def 函数名(参数列表[变量名:类型]...):返回值类型 = {函数体,表达式}

参数默认是val ,每个函数参数后面必须带前缀冒号的类型标注,因为Scala编译器没办法推断函数参数类型;但是可以进行结果类型的推断;

scala>def max(x:Int,y:Int)=if(x>y) x else y

遍历或枚举

args.foreach(arg => println(arg))
args.foreach((arg: String) => println(arg))
args.foreach(println)

循环三种方式  [to]   [Range]  [until]

Range可以包含区间上限,也可以不包含区间上限;步长默认为1,也可以指定一个非1的步长

scala> 1 to 10
res26: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> Range(1,10)
res27: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> 1.to(10)
res28: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> Range(1,10,2)
res29: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> Range(1,10,4)
res30: scala.collection.immutable.Range = Range(1, 5, 9)
scala> 1 until 10
res31: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

定义数组

val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
for (i <- 0 to 2)
print(greetStrings(i))

val的数组元素可以被重新赋值
greetStrings(0) = "Hello"
将被转化为
greetStrings.update(0, "Hello")

scala> var arr = new Array[Int](10)
arr: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

scala> arr(1)=3 

scala> arr.mkString(";")
res35: String = 0;3;0;0;0;0;0;0;0;0
 

scala> arr.mkString("<","and",">")
res37: String = <0and3and0and0and0and0and0and0and0and0>

Scala里的数组是通过把索引放在圆括号里面访问的,而不是像Java

样放在方括号里。所以数组的第零个元素是greetStrings(0),不是greetStrings[0]

改变数据元素:yield属性

List集合的相关操作

List(类似java中的LinkedList)基于链表实现,插入元素比较快,随机访问速度稍慢

1.scala.list   “::”右操作数       “:::”叠加属性

2.类List没有提供append操作,因为随着列表变长append的耗时将呈线性增长,而使用::做前缀则仅花费常量时间。如果你想通过添加元素来构造列表,你的选择是把它们前缀进去,当你完成之后再调用reverse;或使用ListBuffer,一种提供append操作的可变列表,当你完成之后调用toList

3. Array和List说明:
    Array和List都不可变,它为何这样设计呢?它最大的好处就是用在高并发的地方。
    因为在高并发时,如果多个线程都访问一个公用的变量,那这个变量就要加共享锁。这里也是最容易出错的地方,不光影响性能的问题。那scala呢,干脆就换了个思路,来解决多线程时并发的问题。它换了什么思路呢?它就是让这个共享变量不可变。那这个集合只能读,不能写,就自然不会存在并发访问问题。所以这两个类要比java中的性能高。当然scala也提供了可变版本,ArrayBuffer对应Array,ListBuffer对应List。

4. Array和List的区别
    Array是连续存储结构,所以初始化时必须设定初始值,List是不连续的存储结构,所以可以不初始化。
    当不确定数组大小时,使用List替代Array;需要大量查找操作时使用Array替代List;当需要进行频繁的插入和删除时使用List代替Array。List相对比Array占用更多空间。查询某个值而言hashtable更快。当然它们结构完全不同,没有可比性。毕竟数组是节约空间,而hash表是散列的,牺牲空间来换取速度。

val list = List[Int]()	//构造一个空元素的集合,元素类型为Int
    val list = List(1,2,3,4);	//构造一个集合,初始化其内容,可以根据初始化的类型判断其类型,所以无需写泛型。
    list.head	//list中的第一个元素
    list(3)		//按下标访问,获取第4个元素,但越往后越慢。
    list.length	//获取链表的长度
    注意:和java不同的是,它的内容不可变。 list(3)=40这是不行的,它是只读的。换成var list也不行,它们概念完全不同。看看它的内存分配。List(1,2,3,4,5)它在堆内存中,除了基本变量和局部变量都是在堆内存中。如main方法中的局部变量使用的是栈内存。val也好,var也好这个变量的引用都在它都在栈内存。变量在堆内存中,只是说这个变量的引用指向堆内存。var只是说这个引用的地址可以改变,val是引用的地址不能发生变更。

    既然是不可变的集合,那需要增加一个元素怎么做呢?
    list.:+(6)	//list :+ 6,它会创建一个新的list,最后一个元素是6。这点和java中String的处理如出一辙。
    注意实现方式,计算现有集合然后转换成一个新的集合,这个思想是scala乃至spark都广泛使用,如spark中的RDD也是这个思想。

    那在list前面加元素呢?
    val list3 = 6 +: list	//这就是在前还是在后加,冒号跟着list走

    合并两个list为一个list
    val lista = List(1,2)
    val listb = List(5,6)
    println(lista ::: listb)	//使用三个冒号连接
    如果listb = List(1,2)呢?	//list是可以有重复元素

    中间插入元素
    println(list.take(2) ::: List(0) ::: list.takeRight(2))
    take(2)取list集合的前2个,takeRight(2)取list集合的后两个元素
	 
	val list = List(1, 2, 3, 4, 5)
	val l = list.take(2) ::: list.takeRight(2)
	--》l: List[Int] = List(1, 2, 4, 5)

    删除左侧元素
    println(list.drop(2))		    //删除左边的2个元素
    删除右侧元素
    println(list.dropRight(2))		//删除右边的2个元素
    更新元素
    println(list.updated(2,30))	//更新第三个元素为30。其实它是创建了一个新的list,而不是真正更新了。

ArrayBuffer可变数组
//ArrayBuffer需要导入包,mutable可变,immutable不可变
    import scala.collection.mutable.ArrayBuffer

    val buffer = ArrayBuffer(1,2,3,4,5)
    buffer += 6
    println(buffer)
    结果:ArrayBuffer(1,2,3,4,5,6)

    它就非常类似java中的String和StringBuffer。String是不可变的,StringBuffer是可变的。
    注意:java下导入包是java.*,而scala导入时scala.collection._

Set
元素不能重复,重复的会自动剔除,无序

val set = Set(1,2,3,4,45,3,2,6)
println(set)
    结果:Set(45, 1, 2, 6, 3, 4)

Scala中创建集的方法与创建列表和数组的类似:通过调用Set伴生对象的名为apply的工厂方法。代码中,对scala.collection.immutable.Set的伴生对象调用了apply方法,返回了一个缺省的,不可变Set的实例。Scala编译器推断jetSet的类型为不可变Set[String]


Map
    val map = Map(1 -> "北京", 2 -> "上海")    //->前面是key,后面是值
    val map2 = map + (3 -> "广州")            //产生新的map
    val map3 = map -2                        //删除key=2的元素
    println(map)
    println(map2)
    println(map(1))
    结果:
    Map(1 -> "北京", 2 -> "上海")
    Map(1 -> "北京", 2 -> "上海", 3 –> "广州")

MapScala里另一种有用的集合类。和集一样,Scala采用了类继承机制提供了可变的和不可变的两种版本的Map,你能在图3.3里看到,Map的类继承机制看上去和Set的很像。scala.collection包里面有一个基础Map特质和两个子特质Map:可变的Map在scala.collection.mutable里,不可变的在scala.collection.immutable里。


Tuple元组
代表键值对,一般的键值对是一个键一个值,tuple一个键可以有多个值。

元组:tuple。与列表一样,元组也是不可变的,但与列表不同,元组可以包含不同类型的元素。而列表应该是List[Int]List[String]的样子,元组可以同时拥有IntString。元组很有用,比方说,如果你需要在方法里返回多个对象。Java里你将经常创建一个JavaBean样子的类去装多个返回值,Scala里你可以简单地返回一个元组。而且这么做的确简单:实例化一个装有一些对象的新元组,只要把这些对象放在括号里,并用逗号分隔即可。一旦你已经实例化了一个元组,你可以用点号,下划线和一个基于1的元素索引访问它。

访问元组的元素:

    你或许想知道为什么你不能像访问List里的元素那样访问元组的,就像pair(0)。那 是因为List的apply方法始终返回同样的类型,但是元组里的或许类型不同。_1可以有一个结果类型,_2是另外一个,诸如此类。这些数字是基于1的,而不是基于0的,因为对于拥有静态类型元组的其他语言,如Haskell和ML,从1开始是传统的设定。


尽管理论上你可以创建任意长度的元组,然而当前Scala库仅支持到Tupe22。


参考文献:《Scala编程》

博客:https://blog.csdn.net/cris_zz/article/details/85001952

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值