第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")

运行结果

colors1: Set(Blue, Green, Red)
colors2: Set(Blue, Green, Red, Black)
colors1: Set(Blue, Green, Red)
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

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")

UsingCollections/UsingSet.scala

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

运行结果

blog feeds: blog.toolshed.com, blog.agiledeveloper.com

UsingCollections/UsingSet.scala

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

运行结果

# of merged feeds: 4

UsingCollections/UsingSet.scala

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

运行结果

common feeds: blog.toolshed.com

UsingCollections/UsingSet.scala

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

运行结果

One url: http://blog.toolshed.com

UsingCollections/UsingSet.scala

println("Refresh Feeds:")
feeds1.foreach { feed =>
  println(s"  Refreshing $feed...")
}

运行结果

Refresh Feeds:
  Refreshing blog.toolshed.com...
  Refreshing pragdave.me...
  Refreshing blog.agiledeveloper.com...

8.3 关联映射

UsingCollections/PlayingWithMap.scala

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

UsingCollections/PlayingWithMap.scala

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

运行结果

# of Filtered: 1

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)

运行结果

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

UsingCollections/PlayingWithMap.scala

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

运行结果

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

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")
}

运行结果

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

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"))

运行结果

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

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}")

运行结果

Number of forums: 2

8.4 不可变列表

UsingCollections/PlayingWithList.scala

val feeds = List("blog.toolshed.com", "pragdave.me", "blog.agiledeveloper.com")

UsingCollections/PlayingWithList.scala

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

运行结果

First feed: blog.toolshed.com
Second feed: pragdave.me

UsingCollections/PlayingWithList.scala

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

运行结果

First Feed In Prefixed: forums.pragprog.com/forums/87

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}")

运行结果

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

UsingCollections/PlayingWithList.scala

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

运行结果

Last Feed In Appended: agilelearner.com

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")}")

运行结果

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

UsingCollections/PlayingWithList.scala

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

运行结果

Feed url lengths: 17, 11, 23

UsingCollections/PlayingWithList.scala

val total = feeds.foldLeft(0) { (total, feed) =>
  total + feed.length
}
println(s"Total length of feed urls: $total")

运行结果

Total length of feed urls: 51

UsingCollections/PlayingWithList.scala

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

运行结果

Total length of feed urls: 51

UsingCollections/PlayingWithList.scala

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

运行结果

Total length of feed urls: 51

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")
}

UsingCollections/Colon.scala

val cow = new Cow
val moon = new Moon

cow ^ moon
cow ^: moon

运行结果

Cow jumped over the moon
This cow jumped over the moon too

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

运行结果

Called unary +
called unary -
called unary !
called unary ~

8.6 for 表达式

UsingCollections/PowerOfFor.scala

for (_ <- 1 to 3) { print("ho ") }

UsingCollections/PowerOfFor.scala

val result =
  for (i <- 1 to 10)
    yield i * 2

UsingCollections/PowerOfFor.scala

val result2 = (1 to 10).map(_ * 2)

UsingCollections/PowerOfFor.scala

val doubleEven =
  for (i <- 1 to 10; if i % 2 == 0)
    yield i * 2

UsingCollections/PowerOfFor.scala

for {
  i <- 1 to 10
  if i % 2 == 0
} yield i * 2

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(", "))

运行结果

Sletten, Ford, Davis, Halloway

UsingCollections/MultipleLoop.scala

for (i <- 1 to 3; j <- 4 to 6) {
  print(s"[$i,$j] ")
}

运行结果

[1,4] [1,5] [1,6] [2,4] [2,5] [2,6] [3,4] [3,5] [3,6]