第8章——集合

Scala 标准库包含了一组丰富的集合类,以及用于组合、遍历和提取元素的强大操作。在 创建 Scala 应用程序时,会经常用到这些集合。如果想要在使用 Scala 时更加具有生产力,彻 底地学习这些集合是很有必要的。

8.1 常见的 Scala 集合

UsingCollections/UsingSet.scala

val colors1 = Set("Blue", "Green", "Red")
println(s"colors1: $colors1")

val colors2 = colors1 + "Black"
println(s"colors2: $colors2")
println(s"colors1: $colors1")
Full source at GitHub

运行结果

colors1: Set(Blue, Green, Red)
colors2: Set(Blue, Green, Red, Black)
colors1: Set(Blue, Green, Red)
Full source at GitHub
scala>  val colors = Set("Blue", "Green", "Red")
colors: scala.collection.immutable.Set[String] = Set(Blue, Green, Red)

scala> colors.getClass
res0: Class[_ <: scala.collection.immutable.Set[String]] = class 
scala.collection.immutable.Set$Set3

scala> :quit
Full source at GitHub

8.2 使用 Set

UsingCollections/UsingSet.scala

val feeds1 = Set("blog.toolshed.com", "pragdave.me", "blog.agiledeveloper.com")
val feeds2 = Set("blog.toolshed.com", "martinfowler.com/bliki")
Full source at GitHub

UsingCollections/UsingSet.scala

val blogFeeds = feeds1 filter (_ contains "blog")
println(s"blog feeds: ${blogFeeds.mkString(", ")}")
Full source at GitHub

运行结果

blog feeds: blog.toolshed.com, blog.agiledeveloper.com
Full source at GitHub

UsingCollections/UsingSet.scala

val mergedFeeds = feeds1 ++ feeds2
println(s"# of merged feeds: ${mergedFeeds.size}")
Full source at GitHub

运行结果

# of merged feeds: 4
Full source at GitHub

UsingCollections/UsingSet.scala

val commonFeeds = feeds1 & feeds2
println(s"common feeds: ${commonFeeds.mkString(", ")}")
Full source at GitHub

运行结果

common feeds: blog.toolshed.com
Full source at GitHub

UsingCollections/UsingSet.scala

val urls = feeds1 map ("http://" + _)
println(s"One url: ${urls.head}")
Full source at GitHub

运行结果

One url: http://blog.toolshed.com
Full source at GitHub

UsingCollections/UsingSet.scala

println("Refresh Feeds:")
feeds1 foreach { feed ⇒ println(s"  Refreshing $feed...") }
Full source at GitHub

运行结果

Refresh Feeds:
  Refreshing blog.toolshed.com...
  Refreshing pragdave.me...
  Refreshing blog.agiledeveloper.com...
Full source at GitHub

8.3 关联映射

UsingCollections/PlayingWithMap.scala

val feeds = Map(
  "Andy Hunt" -> "blog.toolshed.com",
  "Dave Thomas" -> "pragdave.me",
  "NFJS" -> "nofluffjuststuff.com/blog")
Full source at GitHub

UsingCollections/PlayingWithMap.scala

val filterNameStartWithD = feeds filterKeys (_ startsWith "D")
println(s"# of Filtered: ${filterNameStartWithD.size}")
Full source at GitHub

运行结果

# of Filtered: 1
Full source at GitHub

UsingCollections/PlayingWithMap.scala

val filterNameStartWithDAndPragprogInFeed = feeds filter { element ⇒
  val (key, value) = element
  (key startsWith "D") && (value contains "pragdave")
}
print("# of feeds with auth name D* and pragdave in URL: ")
println(filterNameStartWithDAndPragprogInFeed.size)
Full source at GitHub

运行结果

# of feeds with auth name D* and pragprog in URL: 1
Full source at GitHub

UsingCollections/PlayingWithMap.scala

println(s"Get Andy's Feed: ${feeds.get("Andy Hunt")}")
println(s"Get Bill's Feed: ${feeds.get("Bill Who")}")
Full source at GitHub

运行结果

Get Andy's Feed: Some(blog.toolshed.com)
Get Bill's Feed: None
Full source at GitHub

UsingCollections/PlayingWithMap.scala

try {
  println(s"Get Andy's Feed Using apply(): ${feeds("Andy Hunt")}")
  print("Get Bill's Feed: ")
  println(feeds("Bill Who"))
} catch {
  case _: java.util.NoSuchElementException ⇒ println("Not found")
}
Full source at GitHub

运行结果

Get Andy's Feed Using apply(): blog.toolshed.com
Get Bill's Feed: Not found
Full source at GitHub

UsingCollections/PlayingWithMap.scala

val newFeeds1 = feeds.updated("Venkat Subramaniam", "blog.agiledeveloper.com")
println("Venkat's blog in original feeds: " + feeds.get("Venkat Subramaniam"))
println("Venkat's blog in new feed: " + newFeeds1("Venkat Subramaniam"))
Full source at GitHub

运行结果

Venkat's blog in original feeds: None
Venkat's blog in new feed: blog.agiledeveloper.com
Full source at GitHub

UsingCollections/PlayingWithMap.scala

val mutableFeeds = scala.collection.mutable.Map(
  "Scala Book Forum" -> "forums.pragprog.com/forums/87")
mutableFeeds("Groovy Book Forum") = "forums.pragprog.com/forums/246"
println(s"Number of forums: ${mutableFeeds.size}")
Full source at GitHub

运行结果

Number of forums: 2
Full source at GitHub

8.4 不可变列表

UsingCollections/PlayingWithList.scala

val feeds = List("blog.toolshed.com", "pragdave.me", "blog.agiledeveloper.com")
Full source at GitHub

UsingCollections/PlayingWithList.scala

println(s"First feed: ${feeds.head}")
println(s"Second feed: ${feeds(1)}")
Full source at GitHub

运行结果

First feed: blog.toolshed.com
Second feed: pragdave.me
Full source at GitHub

UsingCollections/PlayingWithList.scala

val prefixedList = "forums.pragprog.com/forums/87" :: feeds
println(s"First Feed In Prefixed: ${prefixedList.head}")
Full source at GitHub

运行结果

First Feed In Prefixed: forums.pragprog.com/forums/87
Full source at GitHub

UsingCollections/PlayingWithList.scala

val feedsWithForums =
  feeds ::: List(
    "forums.pragprog.com/forums/87",
    "forums.pragprog.com/forums/246")
println(s"First feed in feeds with forum: ${feedsWithForums.head}")
println(s"Last feed in feeds with forum: ${feedsWithForums.last}")
Full source at GitHub

运行结果

First feed in feeds with forum: blog.toolshed.com
Last feed in feeds with forum: forums.pragprog.com/forums/246
Full source at GitHub

UsingCollections/PlayingWithList.scala

val appendedList = feeds ::: List("agilelearner.com")
println(s"Last Feed In Appended: ${appendedList.last}")
Full source at GitHub

运行结果

Last Feed In Appended: agilelearner.com
Full source at GitHub

UsingCollections/PlayingWithList.scala

println(s"Feeds with blog: ${feeds.filter(_ contains "blog").mkString(", ")}")
println(s"All feeds have com: ${feeds.forall(_ contains "com")}")
println(s"All feeds have dave: ${feeds.forall(_ contains "dave")}")
println(s"Any feed has dave: ${feeds.exists(_ contains "dave")}")
println(s"Any feed has bill: ${feeds.exists(_ contains "bill")}")
Full source at GitHub

运行结果

Feeds with blog: blog.toolshed.com, blog.agiledeveloper.com
All feeds have com: false
All feeds have dave: false
Any feed has dave: true
Any feed has bill: false
Full source at GitHub

UsingCollections/PlayingWithList.scala

println(s"Feed url lengths: ${feeds.map(_.length).mkString(", ")}")
Full source at GitHub

运行结果

Feed url lengths: 17, 11, 23
Full source at GitHub

UsingCollections/PlayingWithList.scala

val total = feeds.foldLeft(0) { (total, feed) ⇒ total + feed.length }
println(s"Total length of feed urls: $total")
Full source at GitHub

运行结果

Total length of feed urls: 51
Full source at GitHub

UsingCollections/PlayingWithList.scala

val total2 = (0 /: feeds) { (total, feed) ⇒ total + feed.length }
println(s"Total length of feed urls: $total2")
Full source at GitHub

运行结果

Total length of feed urls: 51
Full source at GitHub

UsingCollections/PlayingWithList.scala

val total3 = (0 /: feeds) { _ + _.length }
println(s"Total length of feed urls: $total3")
Full source at GitHub

运行结果

Total length of feed urls: 51
Full source at GitHub

8.5 方法名约定

UsingCollections/Colon.scala

class Cow {
  def ^(moon: Moon): Unit = println("Cow jumped over the moon")
}
class Moon {
  def ^:(cow: Cow): Unit = println("This cow jumped over the moon too")
}
Full source at GitHub

UsingCollections/Colon.scala

val cow = new Cow
val moon = new Moon

cow ^ moon
cow ^: moon
Full source at GitHub

运行结果

Cow jumped over the moon
This cow jumped over the moon too
Full source at GitHub

UsingCollections/Unary.scala

class Sample {
  def unary_+(): Unit = println("Called unary +")
  def unary_-(): Unit = println("called unary -")
  def unary_!(): Unit = println("called unary !")
  def unary_~(): Unit = println("called unary ~")
}

val sample = new Sample
+sample
-sample
!sample
~sample
Full source at GitHub

运行结果

Called unary +
called unary -
called unary !
called unary ~
Full source at GitHub

8.6 for 表达式

UsingCollections/PowerOfFor.scala

for (_ ← 1 to 3) { print("ho ") }
Full source at GitHub

UsingCollections/PowerOfFor.scala

val result = for (i ← 1 to 10)
  yield i * 2
Full source at GitHub

UsingCollections/PowerOfFor.scala

val result2 = (1 to 10).map(_ * 2)
Full source at GitHub

UsingCollections/PowerOfFor.scala

val doubleEven = for (i ← 1 to 10; if i % 2 == 0)
  yield i * 2
Full source at GitHub

UsingCollections/PowerOfFor.scala

for {
  i ← 1 to 10
  if i % 2 == 0
} yield i * 2
Full source at GitHub

UsingCollections/Friends.scala

class Person(val firstName: String, val lastName: String)
object Person {
  def apply(firstName: String, lastName: String): Person =
    new Person(firstName, lastName)
}
val friends = List(Person("Brian", "Sletten"), Person("Neal", "Ford"),
  Person("Scott", "Davis"), Person("Stuart", "Halloway"))

val lastNames =
  for (friend ← friends; lastName = friend.lastName) yield lastName

println(lastNames.mkString(", "))
Full source at GitHub

运行结果

Sletten, Ford, Davis, Halloway
Full source at GitHub

UsingCollections/MultipleLoop.scala

for (i ← 1 to 3; j ← 4 to 6) {
  print(s"[$i,$j] ")
}
Full source at GitHub

运行结果

[1,4] [1,5] [1,6] [2,4] [2,5] [2,6] [3,4] [3,5] [3,6]
Full source at GitHub