![Scala编程(第5版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/446/43738446/b_43738446.jpg)
第8步 使用列表
函数式编程的重要理念之一是方法不能有副作用。一个方法唯一要做的是计算并返回一个值。这样做的好处是方法不再互相纠缠在一起,因此变得更可靠、更易复用。另一个好处(作为静态类型的编程语言)是类型检查器会检查方法的入参和出参,因此逻辑错误通常都是以类型错误的形式出现的。将这个函数式的哲学应用到对象的世界意味着让对象不可变。
正如你看到的,Scala数组是一个拥有相同类型的对象的可变序列。例如,一个Array[String]只能包含字符串。虽然无法在数组实例化以后改变其长度,但是可以改变它的元素值。因此,数组是可变的对象。
对于需要拥有相同类型的对象的不可变序列的场景,可以使用Scala的List类。与数组类似,一个List[String]只能包含字符串。Scala的List类(即scala.List)与Java的List类(即java.util.List)的不同在于Scala的List类是不可变的,而Java的List类是可变的。更笼统地说,Scala的List类被设计为允许函数式风格的编程。创建列表的方法很简单,如示例3.3所示。
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-069-1.jpg?sign=1739015428-sGRkeFV9F89i8mwK3SLrj4xiIlx6ELAI-0-13b48ffe60044061583a969152e5c380)
示例3.3 创建并初始化一个列表
示例3.3中的代码创建了一个新的名称为oneTwoThree的val,并将其初始化成一个新的拥有整型元素1、2、3的新List[Int]。[3]List类是不可变的,它的行为有些类似于Java的字符串:当你调用List类的某个方法,而这个方法的名称看上去像是会改变列表的时候,它实际上是创建并返回一个带有新值的新列表。例如,List类有个方法叫“:::”,用于列表拼接。用法如下:
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-070-1.jpg?sign=1739015428-AIik2vNIAFcBr4H2FGGDlNj3tA2b1Xmw-0-a701d3e43ad8fbe1b17a9576baeed460)
执行这段脚本,oneTwoThreeFour将会指向List(1, 2, 3, 4),而oneTwo仍指向List(1, 2),threeFour仍指向List(3, 4)。参与计算的两个列表都没有被拼接操作符:::改变,而是返回了值为List(1, 2, 3, 4)的新列表。
也许在列表上用得最多的操作符是“::”,读作“cons”。它在一个已有列表的最前面添加一个新的元素,并返回这个新的列表。例如,如果执行下面这段代码:
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-070-2.jpg?sign=1739015428-OMAzcEyaIIAqm7wpL2ajc6UfHHgYROBV-0-c92b0eaa4cde12af588086b31764a1a4)
oneTwoThree的值将会是List(1, 2, 3)。
注意
在表达式1 :: twoThree中,::是右操作元(right operand,即twoThree这个列表)的方法。你可能会觉得::方法的结合律(associativity)有些奇怪,实际上其背后的规则很简单:如果一个方法被用在操作符表示法(operator notation)中时,如a * b,方法调用默认都发生在左操作元(left operand),除非方法名以冒号(:)结尾。如果方法名的最后一个字符是冒号,该方法的调用会发生在它的右操作元上。因此,在1 :: twoThree中,::方法调用发生在twoThree上,传入的参数是1,就像这样:twoThree.::(1)。关于操作符结合律的更多细节将在5.9节详细介绍。
表示空列表的快捷方式是Nil,初始化一个新的列表的另一种方式是用::将元素连接起来,并将Nil作为最后一个元素。[4]例如,如下脚本会产生与前一个示例相同的输出,即List(1, 2, 3):
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-071-1.jpg?sign=1739015428-qTHNSevN7kOqbkDjAE0wO4YfHqSU6jf6-0-11a619279be6e81d489c33852960b147)
Scala的List类定义了大量有用的方法,一些方法和用途如表3.1所示。我们将在第14章揭示列表的全面功能。
为什么不在列表末尾追加元素
List类的确提供了“追加”(append)操作,写作:+(在第24章有详细介绍),但这个操作很少被使用,因为向列表(末尾)追加元素的操作所需要的时间随着列表的大小线性增加,而使用::在列表的前面添加元素只需要常量时间(constant time)。如果想通过追加元素的方式高效地构建列表,则可以依次在头部添加完成后,调用reverse方法。也可以用ListBuffer,这是一个可变的列表,它支持追加操作,完成后调用toList方法即可。ListBuffer在15.1节有详细介绍。
表3.1 List类的一些方法和用途
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-071-2.jpg?sign=1739015428-X2dT64X04ZbDBpLZ7w4xerWzBQxCzoY1-0-5602acfdb7f9524bdab4ddc0b1ee881e)
续表
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-072-1.jpg?sign=1739015428-vItmG1iwabQcuOxzxVjEE7qgDJmXzrjm-0-2902d79cf04963128b89c00c76a37d45)
续表
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-073-1.jpg?sign=1739015428-fxbYVHqtd7UXoL4jgmrpSz74LIpVJS7a-0-99e3742c8829d7acc363d17fa8331e9f)