8.3 使用Map

之前,我们用Set存储RSS源。假定我们想把作者的名字同RSS源关联起来,可以将其按照键值对的方式存储在Map里:

  1. val feeds = Map("Andy Hunt" -> "blog.toolshed.com",
  2. "Dave Thomas" -> "pragdave.pragprog.com",
  3. "Dan Steinberg" -> "dimsumthinking.com/blog")

如果想找出作者名字以“D”开头的RSS源,可以用filterKeys()方法:

  1. val filterNameStartWithD = feeds filterKeys( _ startsWith "D" )
  2. println("# of Filtered: " + filterNameStartWithD.size)

结果如下:

  1. # of Filtered: 2

另一方面,如果要根据值进行过滤,取代键值过滤,或是跟键值过滤一起使用可以用filter()方法。提供给filter()的函数值接收一个(key, value)的元组,像下面例子这样:

  1. val filterNameStartWithDAndBlogInFeed = feeds filter { element =>
  2. val (key, value) = element
  3. (key startsWith "D") && (value contains "blog")
  4. }
  5. println("# of feeds with auth name D* and blog in URL: " +
  6. filterNameStartWithDAndBlogInFeed.size)

输出如下:

  1. # of feeds with auth name D* and blog in URL: 1

如果要获得一个人的RSS源,用get()即可。由于给定的键值可能没有对应的值,所以get()的返回类型是Option[T],结果可能是Some[T]或是None,其中TMap里值的类型:

  1. println("Get Andy's Feed: " + feeds.get("Andy Hunt"))
  2. println("Get Bill's Feed: " + feeds.get("Bill Who"))

上面代码的输出如下:

  1. Get Andy's Feed: Some(blog.toolshed.com)
  2. Get Bill's Feed: None

另外,还可以用apply()方法获取一个键值对应的值——记住,对类或对象使用括号时,Scala调用的就是这个方法。不过,apply()方法返回的不是Option[T],而是值。不同于get(),如果给定的键值没有对应的值,就会抛出异常。所以,请确保代码放到了一个try-catch块里:

  1. try {
  2. println("Get Andy's Feed Using apply(): " + feeds("Andy Hunt"))
  3. print("Get Bill's Feed: ")
  4. println(feeds("Bill Who"))
  5. }
  6. catch {
  7. case ex : java.util.NoSuchElementException => println("Not found")
  8. }

使用apply()的输出如下:

  1. Get Andy's Feed Using apply(): blog.toolshed.com
  2. Get Bill's Feed: Not found

如果想添加一个RSS源,可以用update()方法。因为我们用的是一个不变容器,update()并不会影响原来的Map。相反,它会返回一个新的Map,包含了新增的元素:

  1. val newFeeds1 = feeds.update("Venkat Subramaniam", "agiledeveloper.
  2. com/blog")
  3. println("Venkat's blog in original feeds: " + feeds.get("Venkat
  4. Subramaniam"))
  5. println("Venkat's blog in new feed: " + newFeeds1("Venkat Subramaniam"))

看一下update()的效果:

  1. Venkat's blog in original feeds: None
  2. Venkat's blog in new feed: agiledeveloper.com/blog

相对于显式调用update(),还可以利用另一个Scala魔法。如果对赋值左边的类或实例使用括号,Scala会自动调用update()方法。所以,X() = b等价于X.update(b)。如果update()的参数多于一个,可以将除了最后一个参数之外的所有参数放到括号里。所以,X(a) = b等价于X.update(a, b)

可以像这样对不变容器使用隐式调用:val newFeed = feeds("author") = "blog"。不过,这样做会因为多重赋值(一个是update(),另一个保存了新建的Map)而丢弃了语义的优雅。如果要从方法里返回一个新建的Map,隐式的update()用起来会很优雅。不过,如果要就地更新Map,使用可变容器隐式调用会更有意义一些。

  1. val mutableFeeds = scala.collection.mutable.Map(
  2. "Scala Book Forum" -> "forums.pragprog.com/forums/87")
  3. mutableFeeds("Groovy Book Forum")= "forums.pragprog.com/forums/55"
  4. println("Number of forums: " + mutableFeeds.size)

输出如下:

  1. Number of forums: 2