Scala笔记(一)

这篇博客介绍了Scala编程的基础知识,包括变量类型(val和var)、字面量、Range使用、printf函数、文件读写、if表达式、for循环、列表、元组、集、映射、迭代器以及类和方法的定义。特别强调了Scala与Java的差异,如数组的访问、不可变性以及构造器的使用。

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

scala

Scala有两种类型的变量,一种是val,是不可变的,在声明时就必须被初始化,而且初始化以后就不能再赋值;另一种是var,是可变的,声明的时候需要进行初始化,初始化以后还可以再次对其赋值

val myStr = "Hello World!"
var myPrice : Double = 9.9
var myPrice = 9.9

字面量包括整数字面量、浮点数字面量、布尔型字面量、字符字面量、字符串字面量、符号字面量、函数字面量和元组字面量。

val i = 123  //123就是整数字面量
Scala允许对“字面量”直接执行方法
5.toString() //产生字符串"5"
"abc".intersect("bcd")  //输出"bc" intersect()方法用来输出两个字符串中都存在的字符

Scala中并没有提供++和–操作符,当需要递增和递减时,可以采用如下方式表达:

i += 1  //将i递增

Range可以支持创建不同数据类型的数值序列,包括Int、Long、Float、Double、Char、BigInt和BigDecimal等。
(1)创建一个从1到5的数值序列,包含区间终点5,步长为1

1 to 5
1.to(5)
//res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)

(2)创建一个从1到5的数值序列,不包含区间终点5,步长为1

1 until 5
//res1: scala.collection.immutable.Range = Range(1, 2, 3, 4)

(3)创建一个从1到10的数值序列,包含区间终点10,步长为2

1 to 10 by 2
//res2: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

(4)创建一个Float类型的数值序列,从0.5f到5.9f,步长为0.3f

0.5f to 5.9f by 0.8f
//res3: scala.collection.immutable.NumericRange[Float] = NumericRange(0.5, 1.3, 2.1, 2.8999999, 3.6999998, 4.5, 5.3)

Scala还带有C语言风格的格式化字符串的printf函数

printf("My name is %s. I hava %d apples and %d eggs.\n","Ziyu",i,j)

写入文本文件
Scala需要使用java.io.PrintWriter实现把数据写入到文本文件

import java.io.PrintWriter
//import java.io.PrintWriter //这行是Scala解释器执行上面语句后返回的结果
val out = new PrintWriter("/usr/local/scala/mycode/output.txt")
//out: java.io.PrintWriter = java.io.PrintWriter@25641d39  这行是Scala解释器执行上面语句后返回的结果
for (i <- 1 to 5) out.println(i)
out.close()  //必须要执行out.close()语句,才会看到output.txt文件被生成

读取文本文件中的行
可以使用Scala.io.Source的getLines方法实现对文件中所有行的读取。

import scala.io.Source
//import scala.io.Source //这行是Scala解释器执行上面语句后返回的结果
val inputFile = Source.fromFile("output.txt")
//inputFile: scala.io.BufferedSource = non-empty iterator  //这行是Scala解释器执行上面语句后返回的结果
val lines = inputFile.getLines //返回的结果是一个迭代器
//lines: Iterator[String] = non-empty iterator  //这行是Scala解释器执行上面语句后返回的结果
for (line <- lines) println(line)
//输出
1
2
3
4
5

有一点与Java不同的是,Scala中的if表达式的值可以赋值给变量

val x = 6
val a = if (x>0) 1 else -1

Scala中的for循环语句格式如下:
for (变量<-表达式) 语句块
其中,“变量<-表达式”被称为“生成器(generator)”。

for (i <- 1 to 5) println(i)

有时候,我们可能不希望打印出所有的结果,我们可能希望过滤出一些满足制定条件的结果,这个时候,就需要使用到称为“守卫(guard)”的表达式。 比如,我们只希望输出1到5之中的所有偶数,可以采用以下语句:

for (i <- 1 to 5 if i%2==0) println(i)

Scala也支持“多个生成器”的情形,可以用分号把它们隔开

for (i <- 1 to 5; j <- 1 to 3) println(i*j)

也可以给每个生成器都添加一个“守卫”

for (i <- 1 to 5 if i%2==0; j <- 1 to 3 if j!=i) println(i*j)

for推导式
有时候,我们需要对上述过滤后的结果进行进一步的处理,这时,就可以采用yield关键字,对过滤后的结果构建一个集合。

for (i <- 1 to 5 if i%2==0) yield i
//res3: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4)

上面这种带有yield关键字的for循环,被称为“for推导式”。这个概念源自函数式编程,也就是说,通过for循环遍历一个或多个集合,对集合中的元素进行“推导”,从而计算得到新的集合,用于后续的其他处理。

定长数组,就是长度不变的数组,在Scala中使用Array进行声明

val intValueArr = new Array[Int](3)  //声明一个长度为3的整型数组,每个数组元素初始化为0
intValueArr(0) = 12 //给第1个数组元素赋值为12
intValueArr(1) = 45  //给第2个数组元素赋值为45
intValueArr(2) = 33 //给第3个数组元素赋值为33

需要注意的是,在Scala中,对数组元素的应用,是使用圆括号,而不是方括号,也就是使用intValueArr(0),而不是intValueArr[0],这个和Java是不同的。
实际上,Scala提供了更加简洁的数组声明和初始化方法

val intValueArr = Array(12,45,33)
val myStrArr = Array("BigData","Hadoop","Spark")

列表

val intList = List(1,2,3)

列表有头部和尾部的概念,可以使用intList.head来获取上面定义的列表的头部,值是1
使用intList.tail来获取上面定义的列表的尾部,值是List(2,3)
可以看出,头部是一个元素,而尾部则仍然是一个列表
由于列表的头部是一个元素,所以,我们可以使用::操作,在列表的头部增加新的元素,得到一个新的列表

val intList = List(1,2,3)
val intListOther = 0::intList

::操作符是右结合的,因此,如果要构建一个列表List(1,2,3),实际上也可以采用下面的方式:

val intList = 1::2::3::Nil

上面代码中,Nil表示空列表。
我们也可以使用:::操作符对不同的列表进行连接得到新的列表

val intList1 = List(1,2)
val intList2 = List(3,4)
val intList3 = intList1:::intList2

Scala还为列表提供了一些常用的方法,比如,如果要实现求和

intList.sum

元组是不同类型的值的聚集。元组和列表不同,列表中各个元素必须是相同类型,而元组可以包含不同类型的元素。

scala> val tuple = ("BigData",2015,45.0)
tuple: (String, Int, Double) = (BigData,2015,45.0)  //这行是Scala解释器返回的执行结果
scala> println(tuple._1)
BigData
scala> println(tuple._2)
2015
scala> println(tuple._3)
45.0

集(set)是不重复元素的集合。列表中的元素是按照插入的先后顺序来组织的,
但是,”集”中的元素并不会记录元素的插入顺序,而是以“哈希”方法对元素的值进行组织,所以,它允许你快速地找到某个元素。
集包括可变集和不可变集,缺省情况下创建的是不可变集,通常我们使用不可变集。

scala> var mySet = Set("Hadoop","Spark")
mySet: scala.collection.immutable.Set[String] = Set(Hadoop, Spark)
scala> mySet += "Scala"  //向mySet中增加新的元素
scala> println(mySet.contains("Scala"))
true

如果要声明一个可变集,则需要引入scala.collection.mutable.Set包

scala> import scala.collection.mutable.Set
//import scala.collection.mutable.Set
scala> val myMutableSet = Set("Database","BigData")
//myMutableSet: scala.collection.mutable.Set[String] = Set(BigData, Database)
scala> myMutableSet += "Cloud Computing"
//res0: myMutableSet.type = Set(BigData, Cloud Computing, Database)
scala> println(myMutableSet)
//Set(BigData, Cloud Computing, Database)

虽然可变集和不可变集都有添加或删除元素的操作,但是,二者有很大的区别。对不可变集进行操作,会产生一个新的集,原来的集并不会发生变化。 而对可变集进行操作,改变的是该集本身

在Scala中,映射(Map)是一系列键值对的集合,也就是,建立了键和值之间的对应关系。在映射中,所有的值,都可以通过键来获取。
映射包括可变和不可变两种,默认情况下创建的是不可变映射,如果需要创建可变映射,需要引入scala.collection.mutable.Map包。

val university = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University","PKU"->"Peking University")
println(university("XMU"))

如果要检查映射中是否包含某个值,可以使用contains方法

val xmu = if (university.contains("XMU")) university("XMU") else 0
println(xmu)

上面我们定义的是不可变映射,是无法更新映射中的元素的,也无法增加新的元素。如果要更新映射的元素,就需要定义一个可变的映射,如下:

import scala.collection.mutable.Map
val university2 = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University","PKU"->"Peking University")
university2("XMU") = "Ximan University" //更新已有元素的值
university2("FZU") = "Fuzhou University" //添加新元素

也可以使用+=操作来添加新的元素

university2 + = ("TJU"->"Tianjin University") //添加一个新元素
university2 + = ("SDU"->"Shandong University","WHU"->"Wuhan University") //同时添加两个新元素

循环遍历映射

for ((k,v) <- 映射) 语句块
for ((k,v) <- university) printf("Code is : %s and name is: %s\n",k,v)

可以只遍历映射中的k或者v

for (k<-university.keys) println(k)
XMU //打印出的结果
THU //打印出的结果
PKU //打印出的结果

把所有值打印出来

for (v<-university.values) println(v)
Xiamen University //打印出的结果
Tsinghua University //打印出的结果
Peking University  //打印出的结果

在Scala中,迭代器(Iterator)不是一个集合,但是,提供了访问集合的一种方法。当构建一个集合需要很大的开销时(比如把一个文件的所有行都读取内存),迭代器就可以发挥很好的作用。
迭代器包含两个基本操作:next和hasNext。next可以返回迭代器的下一个元素,hasNext用于检测是否还有下一个元素。
通常可以通过while循环或者for循环实现对迭代器的遍历。
while循环如下:

val iter = Iterator("Hadoop","Spark","Scala")
while (iter.hasNext) {
    println(iter.next())
}

for循环如下

val iter = Iterator("Hadoop","Spark","Scala")
for (elem <- iter) {
    println(elem)
}

class Counter {
    private var value = 0
    def increment(): Unit = { value += 1} //大括号里面只有一行语句,那么也可以直接去掉大括号
    //还可以去掉返回值类型和等号,只保留大括号
    //def increment() {value += 1}
    def current(): Int = {value}
}

可以使用new关键字来生成对象:

new Counter //或者new Counter()

对于方法的定义,是通过def实现的。上面的代码“def increment(): Unit = { value += 1}”中,increment()是方法,没有参数,
冒号后面的Unit是表示返回值的类型,在Scala中不返回任何值,那么就用Unit表示,相当于Java中的void类型。方法的返回值,不需要靠return语句,
方法里面的最后一个表达式的值就是方法的返回值,比如,上面current()方法里面只有一条语句“value”,那么,value的值就是该方法的返回值。
下面我们新建对象,并调用其中的方法:

val myCounter = new Counter
myCounter.increment() //或者也可以不用圆括号,写成myCounter.increment
println(myCounter.current)

Scala在调用无参方法时,是可以省略方法名后面的圆括号的。

TestCounter.scala

class Counter {
    private var value = 0
    def increment(): Unit = { value += 1}
    def current(): Int = {value}
}
val myCounter = new Counter
myCounter.increment()
println(myCounter.current)

scala TestCounter.scala  //输出为1

scalac TestCounter.scala 编译报错
因为,当我们使用scalac命令对TestCounter.scala进行编译时,必须要求把声明
(比如val myCounter = new Counter以及myCounter.increment()等)都封装在对象中,这也是JVM字节码的要求。
但是,在TestCounter.scala中,这些声明都没有被封装在对象中,所以,无法编译。

TestCounterJVM.scala

class Counter {
    private var value = 0
    def increment(): Unit = { value += 1}
    def current(): Int = {value}
}
object MyCounter{
    def main(args:Array[String]){
        val myCounter = new Counter
        myCounter.increment()
        println(myCounter.current)
    }
}

使用scalac命令编译这个代码文件,并用scala命令执行
scalac TestCounterJVM.scala
scala -classpath . MyCounter //MyCounter是包含main方法的对象名称,这里不能使用文件名称TestCounterJVM

在Scala中,可以通过定义类似getter和setter的方法,分别叫做value和value_=,来访问私有变量

class Counter {
    private var privateValue = 0  //变成私有字段,并且修改字段名称
    def value = privateValue //定义一个方法,方法的名称就是原来我们想要的字段的名称
    def value_=(newValue: Int){
        if (newValue > 0) privateValue = newValue //只有提供的新值是正数,才允许修改
    }
    def increment(step: Int): Unit = { value += step}
    def current(): Int = {value}
}
object MyCounter{
    def main(args:Array[String]){
        val myCounter = new Counter
        println(myCounter.value)  //打印value的初始值
        myCounter.value = 3 //为value设置新的值
        println(myCounter.value)  //打印value的新值 
        myCounter.increment(1) //这里设置步长为1,每次增加1
        println(myCounter.current)
    }
}

主构造器
Scala的每个类都有主构造器。但是,Scala的主构造器和Java有着明显的不同,Scala的主构造器是整个类体,
需要在类名称后面罗列出构造器所需的所有参数,这些参数被编译成字段,字段的值就是创建对象时传入的参数的值。

class Counter(val name: String, val mode: Int) {
    private var value = 0 //value用来存储计数器的起始值    
    def increment(step: Int): Unit = { value += step}
    def current(): Int = {value}
    def info(): Unit = {printf("Name:%s and mode is %d\n",name,mode)}
}
object MyCounter{
    def main(args:Array[String]){       
        val myCounter = new Counter("Timer",2)
        myCounter.info  //显示计数器信息
        myCounter.increment(1)  //设置步长  
        printf("Current Value is: %d\n",myCounter.current) //显示计数器当前值       
    }

辅助构造器
Scala构造器包含1个主构造器和若干个(0个或多个)辅助构造器。
我们首先认识一下辅助构造器。辅助构造器的名称为this,每个辅助构造器都必须调用一个此前已经定义的辅助构造器或主构造器。

class Counter {
    private var value = 0 //value用来存储计数器的起始值
    private var name = "" //表示计数器的名称
    private var mode = 1 //mode用来表示计数器类型(比如,1表示步数计数器,2表示时间计数器)
    def this(name: String){ //第一个辅助构造器
        this() //调用主构造器
        this.name = name
    }
    def this (name: String, mode: Int){ //第二个辅助构造器
        this(name) //调用前一个辅助构造器
        this.mode = mode
    }
    def increment(step: Int): Unit = { value += step}
    def current(): Int = {value}
    def info(): Unit = {printf("Name:%s and mode is %d\n",name,mode)}
}
object MyCounter{
    def main(args:Array[String]){
        val myCounter1 = new Counter  //主构造器
        val myCounter2 = new Counter("Runner") //第一个辅助构造器,计数器的名称设置为Runner,用来计算跑步步数
        val myCounter3 = new Counter("Timer",2) //第二个辅助构造器,计数器的名称设置为Timer,用来计算秒数
        myCounter1.info  //显示计数器信息
        myCounter1.increment(1)     //设置步长  
        printf("Current Value is: %d\n",myCounter1.current) //显示计数器当前值
        myCounter2.info  //显示计数器信息
        myCounter2.increment(2)     //设置步长  
        printf("Current Value is: %d\n",myCounter2.current) //显示计数器当前值
        myCounter3.info  //显示计数器信息
        myCounter3.increment(3)     //设置步长  
        printf("Current Value is: %d\n",myCounter3.current) //显示计数器当前值

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值