Scala基础(三)

这篇讲Scala中的数据容器,掌握这些容器的使用就基本可以上手Scala了,之后的高级功能在实际的项目中体会。

1.元组(Tuples)

元组是一个容器,用于存放两个或多个不同类型的元素。它是不可变的。它自从创建之后就能修改了。它的语法简单,如下所示。

1
2
val twoElement = ("10",true)
val threeElemment = ("10","harry",true)

当你想要把一些不相关的元素聚合在一起时,元组就派上用场了。

  • 当所有元素都是同一类型时,可以使用集合,比如数组或列表。当元素是不同的类型但是之间有联系时,可以使用类,把它们当成类字段来存储。
  • 但是在某些场景下使用类没有必要。比如你想要一个有多个返回值的函数,此时元组比类更合适。

元组的下标从1开始。下面这个例子展示怎么访问元组中的元素。

1
private val value: String = threeElemment._1

image.png

2.Option类型

Option是一种数据类型,用来表示值是可选的,即要么无值要么有值。它要么是样本类Some的实例,要么是单例对象None的实例。Some类的实例可以存储任何类型的数据,用来表示有值。None对象表示无值。

Option类型可以在函数或方法中作为值返回。返回Some(x)表示有值,x是真正的返回值。返回None表示无值。从函数返回的Option类型对象可以用于模式匹配中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object Test {
def main(args: Array[String]): Unit = {
val code = colorCode("orange")
code match {
case Some(c) => println("code for orange is :" +c)
case None => println("code not defined for orange")
}
}
def colorCode(color:String): Option[Int] ={
color match {
case "red" => Some(1)
case "blue" => Some(2)
case "green" => Some(3)
case _ =>None
}
}
}

image.png

使用Option类型有助于避免空指针异常。在很多语言中,NULL用于表示无值。以C++、Java中一个返回整数的函数为例,如果对于给定的参数没有合法的整数可以返回,函数可能返回NULL。调用者如果没有检查返回值是否为NULL,而直接使用,就可能导致程序崩溃。在Scala中,由于有了严格类型检查和Option类型,这样的错误得以避免。

3.集合

集合是一种容器类的数据结构,可以容纳零个或多个元素。它是一种抽象的数据结构。它支持声明式编程。它有方便使用的接口,使用这些接口就不必手动遍历所有的元素了。

Scala有丰富的集合类,集合类包含各种类型。所有的集合类都有同样的接口。Scala的集合类可以分为三类:序列、集合、map。

1.序列(Seq)

序列表示有先后次序的元素集合。由于元素是有次序的,因此可以根据位置来访问集合中的元素。序列是类可迭代集合的特例Iterable。与迭代不同,序列总是具有定义的元素顺序。序列提供了一种apply索引,指数的范围从0一直到length一个序列,序列支持多种方法来找到子序列。

1
2
3
4
5
6
7
8
9
10
11
12
case class Person(name: String)
object Test {
val nums = Seq(1,2,3)
def main(args: Array[String]): Unit = {
val people=Seq(
Person("zhoujian"),
Person("Jerry"),
Person("MJ"))
val x = people.apply(1)
print(x.name)
}
}

1.1在Scala中Seq与List的区别?

Difference between a Seq and a List in Scala

image.png

1.2 How to add (append and prepend) elements to a Seq

Because Seq is immutable, you can’t add elements to an existing Seq. The way you work with Seq is to modify the elements it contains as you assign the results to a new Seq.

Method Description Example
:+ append 1 item oldSeq :+ e
++ append N items oldSeq ++ newSeq
+: prepend 1 item e +: oldSeq
++: prepend N items newSeq ++: oldSeq

Append and prepend examples

These examples show how to use the append and prepend methods:

1
2
3
4
5
6
val v1 = Seq(4,5,6)              # List(4, 5, 6)
val v2 = v1 :+ 7 # List(4, 5, 6, 7)
val v3 = v2 ++ Seq(8,9) # List(4, 5, 6, 7, 8, 9)

val v4 = 3 +: v3 # List(3, 4, 5, 6, 7, 8, 9)
val v5 = Seq(1,2) ++: v4 # List(1, 2, 3, 4, 5, 6, 7, 8, 9)

2.数组(Array)

数组是一个有索引的元素序列。数组中的所有元素都是相同类型的。它是可变的数据结构。可以修改数组中的元素,但是你不能在它创建之后增加元素。它是定长的。

Scala数据类似其他语言中的数组。访问其中的任意一个元素都占用固定的时间。数组的索引从0开始。要访问元素或修改元素,可以通过索引值达成,索引值位于括号内。下面是一个例子。

1
2
3
4
5
6
//初始化数组
var z = new Array[String](3)
//或者
private val array = Array(10,20,30,40)
array(0) = 50
val first = array(0)

关于数组的基本操作如下:

  • 通过索引值访问元素
  • 通过索引值修改元素

3.列表(List)

列表是一个线性的元素列表,其中存放一堆相同类型的元素。它是一种递归的结构,而不像数组(扁平的数据结构)。和数组不同,它是可变的,创建后即不可修改。列表是Scala和其他函数式语言中最常用的一种数据结构。

尽管可以根据索引来访问列表中的元素,但这并不高效。访问时间和元素在列表中的位置成正比。

下面的代码展示了几种创建列表的方式。

1
2
private val xs = List(10,20,30,40)
private val ys: List[Int] = (1 to 100).toList

关于列表的基本操作如下:

  • 访问第一个元素。为此,List类提供了一个叫head的方法。
  • 访问第一个元素之后的所有元素。为此,List类提供了一个叫做tail的方法。
  • 判断列表是否为空。为此,List提供了一个叫做isEmpty的方法,放列表为空时,它返回true。

4.向量(Vector)

向量是一个结合了列表和数组各自特性的类。它拥有数组和列表各自的性能优点。根据索引访问元素占用固定的时间,线性访问元素也占用固定的时间。向量支持快速修改和访问任意位置的元素

下面是一个例子:

1
2
3
4
5
6
//定义一个向量
private val vector = Vector(0,10,20,30,40)
//往向量里面添加值
val v1 = vector:+50
val v2 = vector:+60
val v3 = vector(3)

5.集合(Set)

set是一个无序的集合,其中的每一个元素都不同。它没有重复的元素,而且,也没法通过索引来访问元素,因为它没有索引。

下面是一个例子:

1
2
//创建一个集合
private val set = Set("apple","orange","pear","banana")

集合支持两种基本操作。

  • contains:如果当前集合包含这个元素,则返回true。元素作为参数传递进来。
  • isEmpty:如果当前集合为空,则返回true。

6.map(Map)

map是一个键值对集合。在其他语言中,它也叫做字典、关联数组或者hash map。它是一个高效的数据结构,适合根据键找对应的值。千万不要把它和Hadoop MapReduce中的map混淆了。Hadoop MapReduce中的map指在集合的一种操作。

下面这段代码展示了如何创建并使用map

1
2
3
//创建一个集合    
private val map = Map("USA" -> "Washington D.C","UK"->"London","India"->"New Delhi")
val indiaCapital = map("India")

Scala还有其他集合类,这里就不一一介绍了。然而,只要掌握了以上内容,这些就足以高效的使用Scala了。

4.集合类的高级方法

Scala集合的强大之处就是在于这些高阶方法。这些高阶方法把函数当成参数。需要注意的是,这些高阶方法并没有改变集合。下面介绍一些常用的高阶方法。例子中使用的是List集合,但是所有的Scala集合类都支持这些高阶方法。

1.map

map方法的参数是一个函数,它将这个函数作用域集合中的每一个元素,返回由其返回值所组成的集合。这个返回的集合中的元素个数和集合中的个数一致。然而,返回集合中的元素类型有可能不一样。下面是一个例子:

1
2
3
4
5
6
7
object Test {
def main(args: Array[String]): Unit = {
val xs = List(1,2,3,4)
val ys :List[Double] = xs.map((x:Int) =>x*20.0)
print(ys)
}
}

在上面的例子中,需要注意的是,xs的类型是List[Int],而ys的类型是List[Double]

如果一个函数只有一个参数,那么包裹参数列表的圆括号可以用大括号代替的。下面的两条语句是等价的。

1
2
private val ys1: List[Double] = xs.map((x:Int) => x*20.0)
private val ys2 = xs.map{(x:Int) => x*20.0}

就想之前说的一样,Scala允许以操作符的方式调用任何方法,为了进一步提高代码的可读性。

2.flatMap

Scala集合的flatMap方法类似与map,它的参数是一个函数,它把这个函数作用于集合中的每一个元素,返回另外一个集合。这个函数作用于原函数中一个元素之后会返回一个集合。这样,最后就会得到一个元素都是集合的集合。使用map方法,就是这样的结果。但是使用flatMap会得到一个Seq[Char]的集合。下面是一个使用flatMap的例子,试图比较这两种不同的作用:

1
2
3
4
5
6
7
8
scala> val fruits = Seq("apple", "banana", "orange")
fruits: Seq[String] = List(apple, banana, orange)

scala> fruits.map(_.toUpperCase)
res0: Seq[String] = List(APPLE, BANANA, ORANGE)

scala> fruits.flatMap(_.toUpperCase)
res1: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)

利用flatten方法将map形式转化为flatmap形式。

image.png

3.filter(过滤)

filter方法将创建谓词函数作用域集合中的每一个元素,返回另一个集合,其中只包含计算结果为真的元素。谓词函数指的是返回一个布尔值的函数。它要么返回true,要么返回false。

1
2
3
private val list: List[Int] = (1 to 100).toList
private val intToBoolean: Int => Boolean = _ % 2 == 0
list filter intToBoolean

image.png

4.foreach

forech方法的参数是一个函数,它把这个函数作用于集合中的每一个元素,但是不返回任何东西。它和map类似,唯一区别在于map返回一个集合,而foreach不返回任何东西。由于它的无返回值特性它很少使用。

1
2
private val word: Array[String] = "Scala is fun ".split(" ")
word.foreach(println)

5.reduce

`reduce`方法返回一个值。顾名思义,**它将一个集合整合成一个值**。它的参数是一个函数,这个函数有两个参数,并返回一个值。从本质上说,这个函数是一个二元操作符,并且满足结合律和交换律。
1
2
3
4
5
private val ints = List(2,4,8,10)
private val sum: Int = ints reduce{ (x, y) => x+y}
private val product: Int = ints reduce{ (x, y) => x*y}
private val max: Int = ints reduce{ (x, y) => if (x > y) x else y}
private val min: Int = ints reduce{ (x, y) => if (x < y) x else y}

需要注意的是,Hadoop MapReduce中的map/reduce和我们上面说的map/reduce是相似的。

参考文献

https://docs.scala-lang.org/zh-cn/overviews/

https://alvinalexander.com/scala/seq-class-methods-examples-syntax

文章目录
  1. 1. 1.元组(Tuples)
  2. 2. 2.Option类型
  3. 3. 3.集合
    1. 3.1. 1.序列(Seq)
    2. 3.2. 1.1在Scala中Seq与List的区别?
  4. 4. 1.2 How to add (append and prepend) elements to a Seq
  5. 5. Append and prepend examples
    1. 5.1. 2.数组(Array)
    2. 5.2. 3.列表(List)
    3. 5.3. 4.向量(Vector)
    4. 5.4. 5.集合(Set)
    5. 5.5. 6.map(Map)
  6. 6. 4.集合类的高级方法
    1. 6.1. 1.map
    2. 6.2. 2.flatMap
    3. 6.3. 3.filter(过滤)
    4. 6.4. 4.foreach
    5. 6.5. 5.reduce
  7. 7. 参考文献