Scala有一個非常通用,豐富,強大,可組合的集合庫;集合是高階的(high level)并暴露了一大套操作方法。很多集合的處理和轉換可以被表達的簡潔又可讀,但不審慎地用它們的功能也會導致相反的結果。每個Scala程序員應該閱讀 集合設計文檔;通過它可以很好地洞察集合庫,并了解設計動機。
總使用最簡單的集合來滿足你的需求
### 層級
集合庫很大:除了精心設計的層級(Hierarchy)——根是 Traversable[T] —— 大多數集合都有不可變(immutable)和可變(mutable)兩種變體。無論其復雜性,下面的圖表包含了可變和不可變集合層級的重要差異。

Iterable[T] 是所有可遍歷的集合,它提供了迭代的方法(foreach)。Seq[T] 是有序集合,Set[T]是數學上的集合(無序且不重復),Map[T]是關聯數組,也是無序的。
### 集合的使用
*優先使用不可變集合*。不可變集合適用于大多數情況,讓程序易于理解和推斷,因為它們是引用透明的( referentially transparent )因此缺省也是線程安全的。
*使用可變集合時,明確地引用可變集合的命名空間*。不要用使用import scala.collection.mutable._ 然后引用 Set ,應該用下面的方式替代:
~~~
import scala.collections.mutable
val set = mutable.Set()
~~~
這樣就很明確在使用一個可變集合。
*使用集合類型缺省的構造函數*。每當你需要一個有序的序列(不需要鏈表語義),用 Seq() 等諸如此類的方法構造:
~~~
val seq = Seq(1, 2, 3)
val set = Set(1, 2, 3)
val map = Map(1 -> "one", 2 -> "two", 3 -> "three")
~~~
這種風格從語意上分離了集合與它的實現,讓集合庫使用更適當的類型:你需要Map,而不是必須一個紅黑樹(Red-Black Tree,注:紅黑樹TreeMap是Map的實現者)
此外,默認的構造函數通常使用專有的表達式,例如:Map() 將使用有3個成員的對象(專用的Map3類)來映射3個keys。
上面的推論是:在你自己的方法和構造函數里,適當地接受最寬泛的集合類型。通常可以歸結為Iterable, Seq, Set, 或 Map中的一個。如果你的方法需要一個 sequence,使用 Seq[T],而不是List[T]
### 風格
函數式編程鼓勵使用流水線轉換將一個不可變的集合塑造為想要的結果。這常常會有非常簡明的方案,但也容易迷糊讀者——很難領悟作者的意圖,或跟蹤所有隱含的中間結果。例如,我們想要從一組語言中匯集不同的程序語言的投票,按照得票的順序顯示(語言,票數):
~~~
val votes = Seq(("scala", 1), ("java", 4), ("scala", 10), ("scala", 1), ("python", 10))
val orderedVotes = votes
.groupBy(_._1)
.map { case (which, counts) =>
(which, counts.foldLeft(0)(_ + _._2))
}.toSeq
.sortBy(_._2)
.reverse
~~~
上面的代碼簡潔并且正確,但幾乎每個讀者都不能理解作者的原本意圖。一個策略是聲明中間結果和參數:
~~~
val votesByLang = votes groupBy { case (lang, _) => lang }
val sumByLang = votesByLang map { case (lang, counts) =>
val countsOnly = counts map { case (_, count) => count }
(lang, countsOnly.sum)
}
val orderedVotes = sumByLang.toSeq
.sortBy { case (_, count) => count }
.reverse
~~~
代碼也同樣簡潔,但更清晰的表達了轉換的發生(通過命名中間值),和正在操作的數據的結構(通過命名參數)。如果你擔心這種風格污染了命名空間,用大括號{}來將表達式分組:
~~~
val orderedVotes = {
val votesByLang = ...
...
}
~~~
### 性能
高階集合庫(通常也伴隨高階構造)使推理性能更加困難:你越偏離直接指示計算機——即命令式風格——就越難準確預測一段代碼的性能影響。然而推理正確性通常很容易;可讀性也是加強的。在Java運行時使用Scala使得情況更加復雜,Scala對你隱藏了裝箱(boxing)/拆箱(unboxing)操作,可能引發嚴重的性能或內存空間問題。
在關注于低層次的細節之前,確保你使用的集合適合你。 確保你的數據結構沒有不期望的漸進復雜度。各種Scala集合的復雜性描述在[這兒](http://www.scala-lang.org/docu/files/collections-api/collections_40.html)。
性能優化的第一條原則是理解你的應用為什么這么慢。不要使用空數據操作。在執行前分析[[1]](http://twitter.github.io/effectivescala/index-cn.html#fn1 "Jump to note 1")你的應用。關注的第一點是熱循環(hot loops) 和大型的數據結構。過度關注優化通常是浪費精力。記住Knuth(高德納)的格言:“過早優化是萬惡之源”。
如果是需要更高性能或者空間效率的場景,通常更適合使用低級的集合。對大序列使用數組替代列表(List) (不可變Vector提供了一個指稱透明的轉換到數組的接口) ,并考慮使用buffers替代直接序列的構造來提高性能。
### Java集合
使用 scala.collection.JavaConverters 與Java集合交互。它有一系列的隱式轉換,添加了asJava和asScala的轉換方法。使用它們這些方法確保轉換是顯式的,有助于閱讀:
~~~
import scala.collection.JavaConverters._
val list: java.util.List[Int] = Seq(1,2,3,4).asJava
val buffer: scala.collection.mutable.Buffer[Int] = list.asScala
~~~