可變和不可變集合
-
Scala
中的集合可分為可變集合和不可變集合。可變集合可以當場被更新,不可變集合本身是不可變的。
-
- 所有的集合類都可以在
scala.collection
或者其子包mutable,immutable
或者generic
中找到。大多數使用的集合分為三個變種,分別對應不同的可變性特征。分別位于scala.collection,scala.collection.immutable
以及scala.collection.mutable
。
- 所有的集合類都可以在
-
scala.collection
中的集合既可以是可變的,也可以是不變的。scala.collection.IndexedSeq[T]
是scala.collection.immutable.IndexedSeq[T]
和scala.collection.mutable.IndexedSeq[T]
的超類。一般來說,collection
中定義了與不可變集合相同的接口,mutable
包中的可變集合會在不可變接口上添加一些有副作用的修改操作。
-
-
Scala
的默認集合實現是不可變集合,因為scala包引入的默認綁定是不可變的。
-
集合的一致性
- 每一種集合都可以使用一致的語法創建對象,
List(1, 2, 3),Seq(1, 2, 3)
,所有集合的toString
方法也會產生一致的輸出格式。類型名稱加圓括號,其中的元素使用逗號隔開。類的層次結構如下:Traversable Iterable Seq IndexedSeq Vector ResizableArray GenericArray LinearSeq MutableList List Stream Set SortedSet TreeSet HashSet(immutable) LinkedHashSet HashSet(mutable) BitSet EmptySet, Set1, Set2, Set3, Set4 Map SortedMap TreeMap HashMap(immutable) LinkedHashMap HashMap(mutable) EmptyMap, Map1, Map2, Map3, Map4
Traversable特質
-
Traversable
中定義了集合的基本操作,都在TraversableLike
中。唯一需要重寫的方法是foreach:def foreach[U](f: Elem => U)
,事實上,foreach
并不會保存任何計算結果,返回的是Unit
類型。
-
-
-
Traversable
中的方法可以分為以下幾類:
- 添加,
++
- 映射,
map, flatMap, collect
- 轉換,
toIndexedSeq,toIterable,toStream,toArray,toList,toSeq,toSet,toMap
,如果原集合已經匹配則直接返回該集合 -
copy
,copyToBuffer:ArrayBuffer和ListBuffer
和copyToArray
- 大小操作,
isEmpty,nonEmpty,size
和hasDefiniteSize
,如果hasDefiniteSize
為false
,則size
方法會報錯或者根本不返回。 - 元素獲取,
head,last,headOption,lastOption,find
,find
會選中集合中滿足條件的首個元素。并不是所有集合都會有明確的定義,第一個和最后一個。如果某個集合總是以相同的順序交出元素,那么它就是有序的,否則就是無序的;順序通常對測試比較重要,Scala
對所有集合都提供了有序版本。HashSet
的有序版本為LinkedHashSet
。 - 子集獲取操作,
takeWhile,tail,init,slice,take,drop,filter,dropWhile,filterNot,withFilter
- 細分,
splitAt(x take n, x drop n),span(x takeWhile p, x dropWhile p),partition(x filter p, x filterNot p),groupBy
,groupBy
生成的是一個Map
,分類依據為key
,對應的列表為value
,將集合元素切分為若干子集合 - 元素測試,
exists,forAll,count
- 折疊,
foldLeft,foldRight,/:,:\,reduceLeft,reduceRight
- 特殊折疊,
sum,product,min
和max
,用于操作特定類型的集合,數值型或者可比較類型 - 字符串操作,
mkString,addString,stringPrefix
- 視圖操作,
view
,是一個惰性求值的方法
-
Iterable特質
-
-
Iterable
特質的所有方法都是使用iterator
方法來定義的,這個抽象方法的作用是逐個交出集合的元素。Iterable
中還有兩個方法返回迭代器,一個是grouped
,一個是sliding
。
-
List(1,2,3,4,5)
:grouped(3)
交出的是List(1,2,3)
和List(4,5)
-
List(1,2,3,4,5)
:sliding(3)
交出的是List(1,2,3),List(2,3,4),List(3,4,5)
,再使用next
迭代器就會出錯。
-
-
-
Iterable
還對Traversable
添加了其他的一些方法,這些方法只有在有迭代器的情況下才能高效實現。
- 子集合:
takeRight
,包含后n
個元素的集合,如果集合沒有順序,那就是任意的n
個。 -
dropRight
,集合除去takeRight
的部分。 - 拉鏈:
zip,zipAll(ys, x, y)
:xs
長度不夠使用x
填充,ys
長度不夠使用y
填充,zipWithIndex
,sameElements ys
,檢查xs
和ys
是否含有相同順序的相同元素。
-
- 為什么同時會有
Traversable
和Iterable
?
額外增加一層foreach
的原因是有時候提供foreach
比提供Iterator
更容易。
- 為什么同時會有
序列型特質Seq,IndexedSeq和LinearSeq
-
-
Seq
特質代表序列,序列是一種有長度并且元素有固定下標位置的Iterable
。
- 下標和長度操作:
apply
,isDefinedAt
:測試入參是否在indices
中,length
:集合類通用size
的別名,indices
,lengthCompare
:用于比較兩個集合的長度,無限長度的也可以比較,Seq[T]
是一個偏函數 - 下標檢索操作:
indexOf,lastIndexOf,indexOfSlice,lastIndexOfSlice,indexWhere,lastIndexWhere,segmentLength(p,i)
:從xs(i)
開始滿足p
的片段的長度,prefixLength(p)
,xs
中滿足p
的最長前綴長度。 - 添加:
+:,:+,padTo
- 更新:
updated,patch
,對mutable.Seq
而言還有一個update
操作,s(1)=8
,可以當場更改元素,但是updated
不會。 - 排序:
sorted,sortwith
:使用lessThan
函數進行比較,sortBy
:對元素應用f
后進行排序得到 - 反轉:
reverse,revereIterator,reverseMap f
:對元素應用f
后進行倒序交出 - 比較:
startsWith,endsWith,contains,corresponds(ys, p)
:查看xs
和ys
對應元素是否滿足p
,containsSlice
- 集合操作:
intersect,diff,union,distinct
-
- 特質
Seq
有兩個子特質,IndexedSeq
和LinearSeq
,并沒有添加新的操作,不過各自擁有不同的性能特征。LinearSeq
擁有高效的head
和tail
操作,IndexedSeq
擁有高效的apply,length
和隨機訪問操作。List
和Stream
都是常用的LinearSeq
,Array
和ArrayBuffer
則是常用的IndexedSeq
。Vector
是混用兩種達到添加和檢索都比較高效的數據結構。
- 特質
緩沖
-
- 可變序列中比較重要的就是緩沖,緩沖允許對已有元素進行更新,同時還允許元素插入,殺出以及在尾部高效地添加元素。兩個常用的
Buffer
是ListBuffer
和ArrayBuffer
,都能夠高效地轉到對應的數據結構,常用的操作有添加和刪除。
- 添加:
+= 3,+= (3,4,5),++= xs,x +=: buf
,添加在頭部,xs ++=: buf,insert(i, x),insertAll(i, xs)
- 刪除操作:
-= 3,remove(i),remove(i, n),trimStart n
:移除緩沖中前n
個元素,trimEnd
:移除緩沖中后n
個元素,clear
- 克隆:
clone
- 可變序列中比較重要的就是緩沖,緩沖允許對已有元素進行更新,同時還允許元素插入,殺出以及在尾部高效地添加元素。兩個常用的
集
-
-
Set
是沒有重復元素的Iterable
,主要的操作有:
- 測試:
contains,apply
等同于contains
,subsetOf
- 添加:
+
和++
- 移除:
-
和--
- 集合操作:
intersect,union,diff
,符號版本的有&,|,&~
-
-
- 可變集合擁有就地修改的能力,擁有的方法是:
- 添加:
+=,++=,add
- 移除:
-=,--=,remove,clear,retain
- 更新:
update(elem, boolean)
,如果boolean
為true
,就把elem
放入到set
中;如果boolean
為false
,就把elem
從set
中移除。
- 可變集合還提供了
add
和remove
作為+=
和-=
的變種,add
和remove
返回值表示該操作是否讓集合發生了改變。
- 可變集合還提供了
- 目前可變集的默認實現使用了哈希表來保存集的元素,不可變集的默認實現使用了一種可以跟集的元素數量適配的底層表示。空集使用單個對象表示,
4
個元素以內的集合使用Set1,Set2,Set3,Set4
的對象表示,更多元素的集使用哈希字典樹實現。4
個元素以內的集,不可變集合的實現比可變集合的實現更緊湊。如果使用到的集合比較小,盡量使用不可變集。
- 目前可變集的默認實現使用了哈希表來保存集的元素,不可變集的默認實現使用了一種可以跟集的元素數量適配的底層表示。空集使用單個對象表示,
映射
-
-
Map
是由鍵值對組成的Iterable
,Predef
中提供了一個隱式轉換,可以使用key -> value
這樣的寫法表示(key, value)
這個對偶。Map
的操作和Set
基本相似,可變Map
提供額外的更多操作。
- 查找:
apply,get,getOrElse,contains,isDefinedAt
- 更新:
+,++,updated
- 移除:
-,--
- 產生子集:
keys,KeySet,KeysIterator,valuesIterator,values
- 變換:
filterKeys,mapValues
-
-
- 可變
Map
的操作:
- 更新:
ms(k) = v,+=,++=,put,getOrElseUpdate
- 移除:
-=,--=,remove,retain
- 變換:
transform
- 復制:
copy
- 可變
-
getOrElseUpdate
通常被用做緩存。getOrElseUpdate(k, f)
,第二個參數是傳名參數,只有在get(k) = None
的時候才會被觸發調用。
-
具體的不可變集合類
列表
- 列表,
head
和tail
以及在頭部添加元素是常量時間,剩余的許多操作都是線性時間。
流
- 流和列表很像,不過其元素是惰性計算的,流可以是無限長的,只有被請求到的元素才會被計算,剩余的特征性能和列表是一樣的。流的構造方法
scala> val str = 1 #:: 2 #:: 3 #:: Stream.empty str: scala.collection.immutable.Stream[Int] = Stream(1, ?)
toList
會強制計算其中的惰性部分。scala> def fibFrom(a: Int, b: Int): Stream[Int] = a #:: fibFrom(b, a + b) scala> val fibs = fibFrom(1, 1).take(7) fibs: scala.collection.immutable.Stream[Int] = Stream(1, ?) scala> fibs.toList res23: List[Int] = List(1, 1, 2, 3, 5, 8, 13)
向量
- 向量可以訪問和修改任意位置的元素。向量使用
:+
和:+
來添加元素,Vector.empty
- 向量可以訪問和修改任意位置的元素。向量使用
- 向量的內部是寬而淺的樹,樹的每個節點上多達
32
個元素或者32
個其他節點。元素個數在32
以內的可以使用單個節點解決,2^10
個元素可以使用一跳解決,對于正常大小的向量,選擇一個元素最多需要5
次基本操作就可以。向量的更新也是實效上的常量時間,只會復制從根節點到受影響元素這條path
上的節點。目前是不可變IndexedSeq
的默認實現。
- 向量的內部是寬而淺的樹,樹的每個節點上多達
不可變的棧
- 很少使用,
push
相當于List
的::
操作,pop
相當于List
的tail
操作
不可變的隊列
-
enqueue(List(1,2,3))
,dequeue
是個元組,第一個元素是出隊的元素,第二個元素是隊列中剩下的元素組成的Queue
。 - 可變隊列的
dequeue
就直接是出隊的元素。
區間
-
range
,它的內部表示占據常量的空間,因為Range
的對象可以使用三個數來表示,start,end,step
。
哈希字典樹
- 是實現高效的不可變集和不可變
Map
的方式,內部類似于向量,每個節點有32
個元素或者32
個節點,但是選擇是基于哈希碼,使用最低5
位找到第一棵子樹,接下來的5
位找到第二棵子樹,當某個節點所有元素的哈希碼各不相同時,這個選擇過程就停止了。因此并不是用到哈希碼的所有部分,哈希字典樹在隨機查找和插入刪除之間有比較好的平衡,因此是不可變集以及映射的默認實現。
位組
-
bit set
是用來表示更大整數的小整數的集合。比如包含3,2,0
的位組可以使用整數1101
表示,轉成十進制就是13
。從內部講,位組使用的是一個64
位的Long
數組,第一個Long
表示0-63
的整數,對位組的操作非常快,測試某個位組是否包含某個值只需要常量時間。
列表映射
-
ListMap
將Map
表示為一個由鍵值對組成的鏈表,很少用,因為標準的不可變Map
幾乎總是比ListMap
快,除非經常使用列表中的首個元素。
具體的可變集合類
數組緩沖
- 數組緩沖包含一個數組和一個大小,對數組緩沖的大部分操作跟數組一樣,因為這些操作只是簡單地訪問和修改底層的數組。數組緩沖可以周期尾部高效地添加數據,非常適用于通過往尾部追加新元素來高效構建大集合的場景。可以使用
ArrayBuffer.toArray
方法構建Array
數組。
列表緩沖
- 列表緩沖和數組緩沖很像,構建之后可以將列表緩沖轉換為列表,內部使用的是鏈表而不是數組。
字符串構造器
- 字符串構造器有助于構建字符串,使用
new StringBuilder
創建即可,可使用toString
方法
鏈表,2.11.0之后已經被deprecated了。
鏈表是由用next
指針鏈接起來的節點組成的可變序列,在Scala
中,并不使用null
表示空鏈表,空鏈表的表示是next
字段指向自己。LinkedList.empty.isEmpty
返回的是true
而不是拋出NullPointerException
的異常。
可變列表
-
MutableList
是由一個鏈表和一個指向該列表末端的空節點組成,這使得往列表尾部的追加操作是常量時間的,因為它免除了遍歷列表來尋找末端的需要,MutableList
目前是scala
的mutable.LinearSeq
的標準實現。
隊列
- 在可變隊列中,
enqueue
是可以使用+=
和++=
來替代的,dequeue
方法返回的是頭部元素。
數組序列
- 數組序列是固定大小的,內部使用
Array[AnyRef]
來存放元素,Scala
中的實現是ArraySeq
類。
棧
- 和不可變版本是一樣的,但是可以就地修改棧
數組棧
-
ArrayStack
是可變棧的另一種實現,內部是一個Array
,在需要時需要改變大小。提供了快速的下標索引,一般而言大多數操作都比普通的可變棧更快。
哈希表
- 哈希表使用數組來存放元素,元素的存放位置取決于該元素的哈希碼。往哈希表中添加元素只需要常量時間,只要數組中不發生碰撞。只要哈希表中的對象能夠按照哈希碼分布地非常均勻,操作就很快。因此
Scala
中默認的可變映射和可變集的實現都是基于哈希表的。HashSet
和HashMap
。
弱哈希映射
- 對于這種哈希映射,垃圾收集器并不會跟蹤映射到其中的鍵的鏈接。如果沒有其他引用指向這個鍵,那么關聯值就會消失。弱哈希映射對于類似緩存這樣的任務十分有用。如果是在常規的
HashMap
中,這個映射會無限增長,所有的鍵都不會被當做垃圾處理,使用弱哈希映射就可以避免這個問題。Scala
中弱哈希的實現是基于Java
中的WeakHashMap
實現的。
并發映射
- 并發映射可以被多個線程同時訪問。除了常見的
Map
操作之外,還提供了如下原子操作:m putIfAbsent(k, v)
如果不存在k,v
的綁定,添加k->v
的綁定;m remove (k,v)
如果k
映射到v
,移除該條目;m replace(k, old, new)
如果k
綁定到old
,將k
關聯的值更新為new
;m replace(k, v)
,如果k
綁定到某個值,將k
關聯的值替換為v
。實現是基于Java
的ConcurrentHashMap
。
可變位組
- 可變位組可以被當前修改,在更新方面比不可變位組高效一點。
數組
- 在
Scala
中數組是一種特殊的集合,一方面,Scala
的數組和Java
的數組一一對應。Scala
的數組Array[Int]
是Java
的int[]
表示,Array[Double]
對應double[]
,另一方面,Scala提供了更多的功能。首先Scala
的數組支持泛型,可以存在Array[T]
,T
是類型參數,其次,Scala
數組和Seq
是兼容的,數組還支持所有的序列操作。 -
Scala
的數據是用的Java
中的數組表示的,如何支持序列的操作,主要是因為使用了隱式轉換。每當數組被用作Seq
的時候,會被隱式的轉為Seq
的子類,這個子類的名稱是mutable.WrappedArray
。另外一個可以被應用的隱式轉換,這個轉換只是簡單地將所有的序列方法添加到數組,而不是將數組本身變成序列。將這個數組被包裝成另一個類型為ArrayOps
的對象,這個對象支持所有的序列方法。這個對象的聲明周期很短,通常在調用完序列方法之后就不再訪問了,存儲空間可以被回收。現代的VM
會完全避免創建這個對象。 - 編譯器會選擇
ArrayOps
的toInt
方法而不是WarppedArray.toInt
方法主要是因為WarppedArray
的隱式轉換定義在LowPriorityImplicits
中,而ArrayOps
的隱式轉換定義在Predef
中,Predef
繼承了LowPriorityImplicits
。 - 在
Scala
中,可以使用Array[T]
這樣的表示,像Array[T]
這樣的泛型數組在運行的時候可以是Array[Int]
等八種基本數據類型數組,也可以是對象的數組。在運行時,當類型為Array[T]
的數組元素被更新或者訪問時,有一系列的類型檢查來決定實際的數據類型,在一定程度上減慢了數組操作。對泛型數組的訪問會比確定類型的數組訪問慢上幾倍。// This is wrong!因為類型擦除時T的真正類型被隱去了 def evenElems[T](xs: Vector[T]): Array[T] = { val arr = new Array[T]((xs.length + 1) / 2) for (i <- 0 until xs.length by 2) arr(i / 2) = xs(i) arr } error: cannot find class tag for element type T val arr = new Array[T]((arr.length + 1) / 2) ^
evenElems
實際的類型參數是什么。使用scala.reflect.ClassTag
的類標簽,類標簽描述的是給定類型"被擦除的類型",對于許多場景,編譯器可以自己生成類標簽,對于完全泛化的場景,通常的做法是使用上下文界定傳入類型標簽,設定了一個隱式參數ClassTag[T]
。如果能夠找到,則可以正確構建數組。// This works import scala.reflect.ClassTag def evenElems[T: ClassTag](xs: Vector[T]): Array[T] = { val arr = new Array[T]((xs.length + 1) / 2) for (i <- 0 until xs.length by 2) arr(i / 2) = xs(i) arr }
ClassTag
對象,但是入參本身是一個類型而且不帶類標簽,則不能使用。scala> def wrap[U](xs: Vector[U]) = evenElems(xs) <console>:9: error: No ClassTag available for U def wrap[U](xs: Vector[U]) = evenElems(xs) ^
U
的ClassTag
對象,但是沒有找到,解決方法,在U
后面使用上下文界定引入:ClassTag
。
字符串
- 字符串轉為序列的隱式轉換也存在兩個,一個是優先級較低的
WarppedString
,將字符串轉換為序列,一個是優先級較高的StringOps
,為字符串添加了所有不支持的序列操作。
性能特征
相等性
- 集合類庫對相等性的處理和哈希的處理方式是一樣的。
- 首先分為三大類,
Seq
,Set
和Map
,不同類下的集合永不相等。
- 首先分為三大類,
- 另一方面如果是有序的序列,在元素相等的情況下,元素的順序也要相等。使用可變元素作為哈希集或者哈希映射的鍵是不好的做法,因為使用鍵的哈希碼來進行查找,如果鍵改變了,哈希碼也就改變了,可能就找不到了,或者別的鍵也改變了,哈希碼恰巧為修改的那個鍵,那么查到的就是錯的。所以一般不推薦使用可變的對象來作為哈希的鍵值。
視圖
- 集合中有
map
,filter
等變換器來接收一個集合并產出一個集合。變換器可以通過兩種主要的方法實現,一種是嚴格的,一種是惰性的。嚴格的變換器會構造出帶有所有元素的新集合,惰性的變換器只是構造出結果集合的一個代理,元素會按需構造。def lazyMap[T, U](coll: Iterable[T], f: T => U) = new Iterable[U] { def iterator = coll.iterator map f }
只有當新集合使用
iterator
方法的時候才會生成新的集合。系統化的方式可以將每個集合轉換成惰性的版本,或者是反過來,這個就是集合視圖。視圖是一種特殊的集合,代表了某個基礎集合,但是使用惰性的方式實現所有的變換器。seq.view方法得到集合的視圖。例如 v map (_ + 1) map (_ * 2)會生成中間結果,如果一次性執行map操作會更快,方法就是將集合轉換為視圖,執行map操作,再將視圖轉回到集合。
(v.view map (_ + 1) map (_ * 2)).force
v.view產生一個SeqView對象,是一個惰性求值的Seq。有兩個類型參數,Int表示該視圖的元素類型,Vector[Int]表示該視圖取回時要轉回的類型集合。
v.view map (_ + 1)得到一個SeqViewM[Int, Seq[]]表示一個含有函數map(+1)的操作需要被應用到v的包裝器,M表示Map操作。map (_ * 2)之后得到SeqViewMM對象,最后使用force可以得到轉換之后的結果。
第一次應用Map的時候就丟失了關于集合類型Vector的信息,因為視圖的代碼非常多,通常只是對一般的集合而不是特定的集合實現視圖。
findPalindrome(words take 1000000)會構造出來一個1000000容量的中間結果,不管回文單詞是否已經發生。會造成大量的不必要的開銷,使用words.view take 1000000可以減少中間結果的開銷。 實驗發現并沒有???
view還沒有看完
迭代器
迭代器不是集合,是逐個訪問集合元素的一種方式。兩個基本操作是
next
和hasNext
,對it.next
的調用會返回迭代器的下一個元素并將迭代器的狀態往前推進一步。對同一個迭代器再次調用next
會交出在前一個返回的基礎上更進一步的元素。如果沒有更多的元素可以返回,那么對next
的調用就會拋出NoSuchElementException
,可以使用hasNext
的方法來檢測是否還有更多的元素可以返回。Scala
的迭代器還提供了Traversable
,Iterable
和Seq
特質中的大部分方法,它們提供了foreach
用來對迭代器返回的每個元素執行給定的過程。迭代器的
foreach
和可遍歷集合Traversable
的同名方法有一個重要的區別,對迭代器調用foreach
,執行完之后會將迭代器留在末端,再次調用next
會拋出異常,但是可遍歷集合的foreach
不會,可以連續兩次執行foreach
。Iterator
中其他操作也會出現同樣的問題,操作完之后迭代器被留在末端。比如說map
方法。只有一個方法允許使用同一個迭代器,duplicate
方法,返回一個元組。這兩個迭代器互相獨立,原始的迭代器it
在這個方法調用完成之后被推到了末端。總體來說,
Iterator
和Traversable
的行為還是比較類似的,因此抽取出來了一個公共的接口為TraversableOnce
,該接口的對象可以使用foreach
進行遍歷,但是遍歷完之后該對象的狀態是不明確的,如果是Iterator
,則迭代器在尾部,如果是Traversable
,則對象保持原狀。迭代器的主要操作,
next,hasNext,grouped,sliding,……
和集合的操作是很相似的。添加的一個主要功能是buffered
,將迭代器轉換為帶緩沖的迭代器。帶緩沖的迭代器等于是有一個前哨,可以使用head
返回迭代器的第一個元素,但是不向前推進迭代器。例如:使用這樣的判斷語句進行判斷的時候,while (iterator.next() >= 2) {}
,會丟失迭代器中的一個元素,第一個<2
的元素是得不到的,正確的方法是使用帶緩沖的iterator.head
的方法來進行探尋。iterator.buffered
得到的迭代器和iterator
本身使用的是同一套迭代器。
從頭構建集合
Collection
集合下面的所有類的伴生對象中都具有配套的apply
方法,可以直接使用List(1,2,3)
或者Map(1->3, 3->4)
這樣的方式來構建集合。對于接口類型,Traversable(1,2,3)
會調用該接口的默認實現。在每一個伴生對象中也定義了一個空對象empty
,List.empty = List()
。Seq
的子類伴生對象中定義有許多新的功能,比如說fill
可填充表達式,tabulate
可填充表達式,iterate
可對指定的元素進行指定次數的計算。
Java和Scala集合的轉換
Scala
提供了Java
和Scala
中主要集合之間的隱式轉換,這些轉換在import collection.JavaConversions._
,轉換的時候使用的是wrapper
的對象,不做copy
的操作,如果從Java
集合轉到了Scala
集合,然后又從Scala
集合轉回到Java
,這兩個集合是同一個對象。在轉換到Java
集合的時候不考慮是否為可變集合,如果在不可變集合上試圖進行可變操作,Java
會拋出異常。-
有雙向轉換:
Iterator ? java.util.Iterator
Iterator ? java.util.Enumeration
Iterable ? java.util.Iterable
Iterable ? java.util.Collection
mutable.Buffer ? java.util.List
mutable.Set ? java.util.Set
mutable.Map ? java.util.Map -
有單向轉換
Seq ? java.util.List
mutable.Seq ? java.util.List
Set ? java.util.Set
Map ? java.util.Map