![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=1738841009-rUzsqd6kXrZ87JBo26Sevft5gGhdjhcf-0-bd29314b4b2d80bc435e2351c41cc026)
示例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=1738841009-cm6Ghp7pUHPdtDRupM3jIE6bS7EIOMAh-0-d858718f048d4329fd3dc7563754d81a)
执行这段脚本,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=1738841009-PDzviWfR8uzXdqI6oP1bJe4IGkOfs5WW-0-fe5dc5fa57b1c013ee181577d0b9f08a)
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=1738841009-9RkGsOdzpq5kgqjzw0USyh44N3F0Ws4h-0-e9dc0b979e4c90a5c07ed18e80a5e41a)
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=1738841009-DP2mlap3euSIURGGfR6jzUXrobtQ6hIE-0-07e836f14f2e781ec8ffb83788e740ca)
续表
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-072-1.jpg?sign=1738841009-1ihZC86pV3RHWavebZaDfaB1ymul083X-0-3e71f72f10fe4f9ce9bfbe727b5315aa)
续表
![](https://epubservercos.yuewen.com/9944D7/23020655409775506/epubprivate/OEBPS/Images/42832-00-073-1.jpg?sign=1738841009-drff4sjXNYlKjyt9kmVcvZCiO0ARctCt-0-4b35f19c1e4f57229ec7c4b5e61411fb)