5.6 Scala标准库

详细讨论OOP后,下面来着手编写有点用的类和方法。随Scala安装了Scala标准库,这个库很大,包含Scala特有的类。本节讨论如下主题:

  • 泛型;
  • 集合;
  • XML处理。

5.6.1 泛型

Java使用表示法ClassName来表示支持泛型的类。本书前面说过,定义有些类(如接口Map)时,需要指定多种类型。对于Map,需要指定它将存储的键和值的类型。

在Scala中,使用表示法ClassName[T]

  1. val aList = List[Int]($text-1, 2, 3, 5)

这将创建一个不可修改的列表,其中包含5个元素。由于指定了List[Int],因此只能在其中添加类型为Int或可向上转换为Int的类的实例。

同样,对于需要指定两种类型的泛型类,可这样定义:

  1. val m = Map[String, String]("key1" -> "value1", "key2" -> "value2")

这里创建了一个映射,它将键"key1""key2"分别映射到字符串值"value1""value2"

5.6.2 集合

Scala标准库提供了很多集合API,这些API分为两大类:

  • 不可修改的集合;
  • 可修改的集合。

不可修改的集合通常位于scala.collection.immutable包中,而可修改的类位于scala.collection.mutable包中。默认自动导入的Scala包包含指向多个不可修改的集合类的引用,因此,如果没有显式地导入不可修改或可修改的集合类,默认使用的将是不可修改的版本。

  • 不可修改的列表

在前面两个示例中,使用的分别是不可修改的列表和映射。这些数据结构将在初始化期间被填充。可使用不可修改的集合来创建新列表,下面的示例基于既有列表的内容创建新列表:

  1. val immutableList = List[Int]($text-1, 2, 3, 4, 5)
  2. val newImmutableList1 = 0 :: immutableList
  3. val newImmutableList2 = immutableList ::: List(6, 7)

乍一看,这可能让人迷惑。第2行创建一个新的列表实例,其中包含0和另一个列表的副本,因此newInstance1ListInt。运算符::左边是要添加到新建实例中的值,而右边是要复制的列表。由于List类针对LIFO(后进先出)操作进行了优化,因此创建新列表实例时,将新元素放在既有列表的元素前面会更容易。

然而,创建新列表时,也可在列表末尾添加新元素。为此可使用运算符:::,但这个运算符两边都必须是列表。在前面的示例中,newImmutableList2将包含如下元素:1234567

请注意,创建新列表时,并没有修改原来的列表aList

  • 可修改的列表

还有可修改的列表版本,这种列表名为ListBuffer,位于scala.collection.mutable包中。完全可以想见,这种版本提供了你熟悉的方法,如append()remove()clear()

  1. import scala.collection.mutable
  2. val aMutableList = mutable.ListBuffer[Int]($text-1, 2, 5)
  3. aMutableList.remove(2)
  4. aMutableList.append(3)
  5. println(aMutableList(0))
  6. println(aMutableList)

这将打印1ListBuffer(1, 2, 3)

在Scala中,一种最佳实践是尽可能使用运算符。下面是使用运算符实现的前一个示例:

  1. import scala.collection.mutable
  2. val aMutableList = mutable.ListBuffer[Int]($text-1, 2, 5)
  3. aMutableList -= 5
  4. aMutableList += 3
  5. println(aMutableList)

运算符-=将指定的值从列表中删除,而+=在列表中添加新值。

5.6 Scala标准库 - 图1 注意,使用运算符-=,必须指定要删除的值。另外,请注意将删除的元素的索引。

在Scala提供的众多集合类中,有一个名为ArrayBuffer的可修改类。这个类在内部是使用数组实现的,因此使用索引来访问其元素的效率要高得多:

  1. import scala.collection.mutable
  2. val aMutableArray = mutable.ArrayBuffer[Int]($text-1, 2, 3)
  3. aMutableArray += 4
  4. println(aMutableArray(3))

这将打印4。与ListBuffer类一样,ArrayBuffer也实现了很多运算符。

  • 不可修改的映射

Scala标准库包含众多实现各不相同的映射类,这里将讨论标准类Map,它是不可修改的:

  1. val immutableMap = Map[Int, String](10 -> "ten", 20 -> "twenty")
  2. println(immutableMap(20))

基于既有的映射创建新的映射实例很容易,下面的示例修改了前面定义的映射:

  1. val newImmutableMap = immutableMap + (30 -> "thirty")

要将两个映射合而为一,可使用运算符++

  1. val combinedMap = newImmutableMap ++ Map[Int, String]
  2. (24 -> "twentyfour")
  • 可修改的映射

Scala标准库中的可修改映射类之一是HashMap,它很像Java类java.lang.HashMap

  1. import scala.collection.mutable
  2. val mutableMap = mutable.HashMap[Int, String](10->"ten",
  3. 50->"fifty")
  4. mutableMap += (100 -> "Hundred", 150 -> "Hundred and fifty")
  5. mutableMap -= 10
  6. println(mutableMap)

这将打印mutableMap.type = Map(50 -> fifty, 100 -> Hundred, 150 -> Hundred and fifty)

5.6 Scala标准库 - 图2 注意,在前面的不可修改映射示例中演示的运算符也可用于HashMap实例。用于HashMap实例时,这些运算符的工作原理不变,也将创建新实例,而不是修改当前HashMap实例。

5.6.3 XML处理

Scala标准库包含一个功能强大的XML库,可帮助创建和使用XML文档。这里将演示如何使用XML字面量来生成XML文档。通过使用这种功能,可直接在Scala源代码中输入XML内容。在幕后,Scala编译器将使用其XML库来填充变量,并验证生成的XML是否有效。

5.6 Scala标准库 - 图3 建议你不要直接在Sacla REPL shell中输入下面的示例,而使用你喜欢的文本编辑器创建一个源代码文件,再将其路径传递给命令scala。直接在REPL shell中输入代码和XML时,这个交互式解析器可能出现故障。

下面是一个简单的示例,它生成一个包含XML的String

  1. val productCode = "PC Monitor"
  2. val qty = 2.toString()
  3. val xmlContent =
  4. <basket>
  5. <line>
  6. <product qty={ qty }>{ productCode }</product>
  7. </line>
  8. </basket>
  9. println(xmlContent)

请注意,在XML元素中使用的所有变量都必须是字符串。另外请注意,在Scala中,字面量2是一个对象,因此可对其调用方法toString()。上述程序生成的输出如下:

  1. <basket>
  2. <line>
  3. <product qty="2">PC Monitor</product>
  4. </line>
  5. </basket>

通过创建返回XML元素的函数,可轻松地将集合添加到XML输出中。返回XML元素的函数应返回一个xml.Elem实例:

  1. def createXMLProduct(productCode: String): xml.Elem = {
  2. <product qty="1">{ productCode }</product>
  3. }
  4. val productCodes = List[String]("Keyboard", "Mouse")
  5. def lines =
  6. <basket>
  7. <products> {
  8. productCodes.map(x => createXMLProduct(x))
  9. }</products>
  10. </basket>
  11. println(lines.toString())

这里没有手动遍历集合,而是调用了方法map。对于集合中的每个元素,方法map都使用传递给它的lambda函数来返回新内容,从而对列表进行转换。在这里,它将包含String的列表productCodes转换为一个包含XML元素的新列表。函数map很好地展示了下一节将更详细地讨论的函数式编程。上述脚本的输出如下:

  1. <basket>
  2. <products>
  3. <product qty="1">Computer Keyboard</product>
  4. <product qty="1">Mouse</product></lines>
  5. </products>
  6. </basket>

5.6 Scala标准库 - 图4 注意,在实际编程中,不应以硬编码的方式将数量(属性qty)指定为1