中国领先的IT技术网站
|
|

Scala编程实例:使用Set和Map

本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第二章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

作者:Martin Odersky等来源:Artima|2009-07-09 00:25


因为Scala致力于帮助你充分利用函数式和指令式风格两方面的好处,它的集合类型库于是就区分了集合类的可变和不可变。例如,数组始终是可变的,而列表始终不可变。当问题讨论到集和映射,Scala同样提供了可变和不可变的替代品,不过用了不同的办法。对于集和映射,Scala把可变性建模在类继承中。

51CTO编辑推荐:Scala编程语言专题

例如,Scala的API包含了集的一个基本特质:trait,特质这个概念接近于Java的接口。Scala于是提供了两个子特质,一个是可变的集,另一个是不可变的集。就如你在图3.2里会看到的,这三个特质都共享同样的简化名,Set。然而它们的全称不一样,因为每个都放在不同的包里。Scala的API里具体的Set类,如图3.2的HashSet类,扩展了要么是可变的,要么不可变的Set特质。(尽管Java里面称为“实现”了接口,在Scala里面称为“扩展”或“混入”了特质。)因此,如果你想要使用HashSet,你可以根据你的需要选择可变的或不可变的变体。创造集的缺省方法展示在代码3.5中:

  1. var jetSet = Set("Boeing", "Airbus")  
  2. jetSet += "Lear"  
  3. println(jetSet.contains("Cessna"))  

代码 3.5 创造,初始化,和使用不可变集

代码3.5的第一行代码里,定义了名为jetSet的新var,并使用了包含两个字串,"Boeing"和"Airbus"的不可变集完成了初始化。就像例子中展示的,Scala中创建集的方法与创建列表和数组的类似:通过调用Set伴生对象的名为apply的工厂方法。代码3.5中,对scala.collection.immutable.Set的伴生对象调用了apply方法,返回了一个缺省的,不可变Set的实例。Scala编译器推断jetSet的类型为不可变Set[String]。

Scala的Set类继承关系

要向集加入新的变量,可以在集上调用+,传入新的元素。可变的和不可变的集都提供了+方法,但它们的行为不同。可变集将把元素加入自身,不可变集将创建并返回一个包含了添加元素的新集。代码3.5中,你使用的是不可变集,因此+调用将产生一个全新集。因此尽管可变集提供的实际上是+=方法,不可变集却不是。本例中,代码的第二行,“jetSet += "Lear"”,实质上是下面写法的简写:

  1. jetSetjetSet = jetSet + "Lear"  

因此在代码3.5的第二行,你用一个包含了"Boeing","Airbus"和"Lear"的新集重新赋值了jetSet这个var。最终,代码3.5的最后一行打印输出了集是否包含字串"Cessna"。(正如你所料到的,输出false。)

如果你需要不可变集,就需要使用一个引用:import,如代码3.6所示:

  1. import scala.collection.mutable.Set  
  2. val movieSet = Set("Hitch", "Poltergeist")  
  3. movieSet += "Shrek"  
  4. println(movieSet) 

代码 3.6 创建,初始化,和使用可变集

代码3.6的第一行里引用了可变Set。就像Java那样,引用语句允许你使用简单名,如Set,以替代更长的,全标识名。结果,当你在第三行写Set的时候,编译器就知道你是指scala.collection.mutable.Set。在那行里,你使用包含字串"Hitch"和"Poltergeist"的新可变集初始化了movieSet。下一行通过在集上调用+=方法向集添加了"Shrek"。正如前面提到的,+=是实际定义在可变集上的方法。如果你想的话,你可以替换掉movieSet += "Shrek"的写法,写成movieSet.+=("Shrek")。

尽管目前为止看到的通过可变和不可变的Set工厂方法制造的缺省的集实现很可能能够满足极大多数的情况,但偶尔你也或许想要个显式的集类。幸运的是,语法是相同的。只要引用你需要的类,并使用它伴生对象的工厂方法即可。例如,如果你需要一个不可变的HashSet,你可以这么做:

  1. import scala.collection.immutable.HashSet  
  2. val hashSet = HashSet("Tomatoes", "Chilies")  
  3. println(hashSet + "Coriander") 


Map是Scala里另一种有用的集合类。和集一样,Scala采用了类继承机制提供了可变的和不可变的两种版本的Map,你能在图3.3里看到,Map的类继承机制看上去和Set的很像。scala.collection包里面有一个基础Map特质和两个子特质Map:可变的Map在scala.collection.mutable里,不可变的在scala.collection.immutable里。

Map的实现,如显示在类继承图3.3里的HashMap,扩展了要么可变,要么不可变特质。你可以使用与那些用在数组,列表和集中的一样的工厂方法去创造和初始化映射。例如,代码3.7展示了可变映射的创造过程:

  1. import scala.collection.mutable.Map  
  2. val treasureMap = Map[Int, String]()  
  3. treasureMap += (1 -> "Go to island.")  
  4. treasureMap += (2 -> "Find big X on ground.")  
  5. treasureMap += (3 -> "Dig.")  
  6. println(treasureMap(2))  

代码 3.7 创造,初始化,和使用可变映射

Scala的Map类继承关系 

代码3.7的第一行里,你引用了可变形式的Map。然后就定义了一个叫做treasureMap的val并使用空的包含整数键和字串值的可变Map初始化它。映射为空是因为你没有向工厂方法传递任何值(“Map[Int, String]()”的括号里面是空的)。 下面的三行里你使用->和+=方法把键/值对添加到Map里。像前面例子里演示的那样,Scala编译器把如1 -> "Go to island"这样的二元操作符表达式转换为(1).->("Go to island.")。因此,当你输入1 -> "Go to island.",你实际上是在值为1的Int上调用->方法,并传入值为"Go to island."的String。这个->方法可以调用Scala程序里的任何对象,并返回一个包含键和值的二元元组。 然后你在把这个元组传递给treasureMap指向的Map的+=方法。最终,最后一行输出打印了treasureMap中的与键2有关的值。如果你执行这段代码,将会打印:

  1. Find big X on ground. 

如果你更喜欢不可变映射,就不用引用任何类了,因为不可变映射是缺省的,代码3.8展示了这个例子:

  1. val romanNumeral = Map(  
  2.  1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V"  
  3. )  
  4. println(romanNumeral(4))  

代码 3.8 创造,初始化,和使用不可变映射

由于没有引用,当你在代码3.8的第一行里提及Map时,你会得到缺省的映射:scala.collection.immutable.Map。传给工厂方法入五个键/值元组,返回包含这些传入的键/值对的不可变Map。如果你执行代码3.8中的代码,将会打印输出IV。

本文节选自《Programming in Scala》

【相关阅读】

  1. Scala编程实例:使用List和Tuple
  2. Scala编程实例:带类型的参数化数组
  3. 初探Scala编程:编写脚本,循环与枚举
  4. 初探Scala编程:解释器,变量及函数定义
  5. 影响Scala语言设计的因素列表
【责任编辑:Reno TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

数据库系统概念

本书是数据库系统方面的经典教材之一。国际上许多著名大学包括斯坦福大学、耶鲁大学、得克萨斯大学、康奈尔大学、伊利诺伊大学、印度理工学...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× 学习达标赢Beats耳机