Iterators
疊代器不是一個容器,更确切的說是逐一通路容器内元素的方法。疊代器it的兩個基本操作是next和hasNext。調用it.next()會傳回疊代器的下一個元素,并且更新疊代器的狀态。在同一個疊代器上再次調用next,會産生一個新元素來覆寫之前傳回的元素。如果沒有元素可傳回,調用next方法會抛出一個NoSuchElementException異常。你可以調用[疊代器]的hasNext方法來查詢容器中是否有下一個元素可供傳回。
讓疊代器it逐個傳回所有元素最簡單的方法是使用while循環:
while (it.hasNext) println(it.next())
Scala為Traversable, Iterable和Seq類中的疊代器提供了許多類似的方法。比如:這些類提供了foreach方法以便在疊代器傳回的每個元素上執行指定的程式。使用foreach方法可以将上面的循環縮寫為:
it foreach println
與往常一樣,for表達式可以作為foreach、map、withFilter和flatMap表達式的替代文法,是以另一種列印出疊代器傳回的所有元素的方式會是這樣:
for (elem <- it) println(elem)
在疊代器或traversable容器中調用foreach方法的最大差別是:當在疊代器中完成調用foreach方法後會将疊代器保留在最後一個元素的位置。是以在這個疊代器上再次調用next方法時會抛出NoSuchElementException異常。與此不同的是,當在容器中調用foreach方法後,容器中的元素數量不會變化(除非被傳遞進來的函數删除了元素,但不贊成這樣做,因為這會導緻意想不到的結果)。
疊代器的其他操作跟Traversable一樣具有相同的特性。例如:疊代器提供了map方法,該方法會傳回一個新的疊代器:
scala> val it = Iterator("a", "number", "of", "words")it: Iterator[java.lang.String] = non-empty iteratorscala> it.map(_.length)res1: Iterator[Int] = non-empty iteratorscala> res1 foreach println1625scala> it.next()java.util.NoSuchElementException: next on empty iterato
r
如你所見,在調用了it.map方法後,疊代器it移動到了最後一個元素的位置。
另一個例子是關于dropWhile方法,它用來在疊代器中找到第一個具有某些屬性的元素。比如:在上文所說的疊代器中找到第一個具有兩個以上字元的單詞,你可以這樣寫:
scala> val it = Iterator("a", "number", "of", "words")it: Iterator[java.lang.String] = non-empty iteratorscala> it dropWhile (_.length < 2)res4: Iterator[java.lang.String] = non-empty iteratorscala> it.next()res5: java.lang.String = number
再次注意it在調用dropWhile方法後發生的變化:現在it指向了list中的第二個單詞”number”。實際上,it和dropWhile傳回的結果res4将會傳回相同的元素序列。
隻有一個标準操作允許重用同一個疊代器:
val (it1, it2) = it.duplicate
這個操作傳回兩個疊代器,每個都相當于疊代器it的完全拷貝。這兩個iterator互相獨立;一個發生變化不會影響到另外一個。相比之下,原來的疊代器it則被指定到元素的末端而無法再次使用。
總的來說,如果調用完疊代器的方法後就不再通路它,那麼疊代器的行為方式與容器是比較相像的。Scala容器庫中的抽象類TraversableOnce使這一特質更加明顯,它是 Traversable 和 Iterator 的公共父類。顧名思義,TraversableOnce 對象可以用foreach來周遊,但是沒有指定該對象周遊之後的狀态。如果TraversableOnce對象是一個疊代器,它周遊之後會位于最後一個元素,但如果是Traversable則不會發生變化。TraversableOnce的一個通常用法是作為一個方法的參數類型,傳遞的參數既可以是疊代器,也可以是traversable。Traversable類中的追加方法++就是一個例子。它有一個TraversableOnce 類型的參數,是以你要追加的元素既可以來自于疊代器也可以來自于traversable容器。
下面彙總了疊代器的所有操作。
Iterator類的操作
WHAT IT IS | WHAT IT DOES |
抽象方法: | |
it.next() | 傳回疊代器中的下一個元素,并将位置移動至該元素之後。 |
it.hasNext | 如果還有可傳回的元素,傳回true。 |
變量: | |
it.buffered | 被緩存的疊代器傳回it的所有元素。 |
it grouped size | 疊代器會生成由it傳回元素組成的定長序列塊。 |
xs sliding size | 疊代器會生成由it傳回元素組成的定長滑動視窗序列。 |
複制: | |
it.duplicate | 會生成兩個能分别傳回it所有元素的疊代器。 |
加法: | |
it ++ jt | 疊代器會傳回疊代器it的所有元素,并且後面會附加疊代器jt的所有元素。 |
it padTo (len, x) | 首先傳回it的所有元素,追加拷貝x直到長度達到len。 |
Maps: | |
it map f | 将it中的每個元素傳入函數f後的結果生成新的疊代器。 |
it flatMap f | 針對it指向的序列中的每個元素應用函數f,并傳回指向結果序列的疊代器。 |
it collect f | 針對it所指向的序列中的每一個在偏函數f上有定義的元素應用f,并傳回指向結果序列的疊代器。 |
轉換(Conversions): | |
it.toArray | 将it指向的所有元素歸入數組并傳回。 |
it.toList | 把it指向的所有元素歸入清單并傳回 |
it.toIterable | 把it指向的所有元素歸入一個Iterable容器并傳回。 |
it.toSeq | 将it指向的所有元素歸入一個Seq容器并傳回。 |
it.toIndexedSeq | 将it指向的所有元素歸入一個IndexedSeq容器并傳回。 |
it.toStream | 将it指向的所有元素歸入一個Stream容器并傳回。 |
it.toSet | 将it指向的所有元素歸入一個Set并傳回。 |
it.toMap | 将it指向的所有鍵值對歸入一個Map并傳回。 |
拷貝: | |
it copyToBuffer buf | 将it指向的所有元素拷貝至緩沖區buf。 |
it copyToArray(arr, s, n) | 将it指向的從第s個元素開始的n個元素拷貝到數組arr,其中後兩個參數是可選的。 |
尺寸資訊: | |
it.isEmpty | 檢查it是否為空(與hasNext相反)。 |
it.nonEmpty | 檢查容器中是否包含元素(相當于 hasNext)。 |
it.size | it可傳回的元素數量。注意:這個操作會将it置于終點! |
it.length | 與it.size相同。 |
it.hasDefiniteSize | 如果it指向的元素個數有限則傳回true(預設等同于isEmpty) |
按下标檢索元素: | |
it find p | 傳回第一個滿足p的元素或None。注意:如果找到滿足條件的元素,疊代器會被置于該元素之後;如果沒有找到,會被置于終點。 |
it indexOf x | 傳回it指向的元素中index等于x的第一個元素。注意:疊代器會越過這個元素。 |
it indexWhere p | 傳回it指向的元素中下标滿足條件p的元素。注意:疊代器會越過這個元素。 |
子疊代器: | |
it take n | 傳回一個包含it指向的前n個元素的新疊代器。注意:it的位置會步進至第n個元素之後,如果it指向的元素數不足n個,疊代器将指向終點。 |
it drop n | 傳回一個指向it所指位置之後第n+1個元素的新疊代器。注意:it将步進至相同位置。 |
it slice (m,n) | 傳回一個新的疊代器,指向it所指向的序列中從開始于第m個元素、結束于第n個元素的片段。 |
it takeWhile p | 傳回一個疊代器,指代從it開始到第一個不滿足條件p的元素為止的片段。 |
it dropWhile p | 傳回一個新的疊代器,指向it所指元素中第一個不滿足條件p的元素開始直至終點的所有元素。 |
it filter p | 傳回一個新疊代器 ,指向it所指元素中所有滿足條件p的元素。 |
it withFilter p | 同it filter p 一樣,用于for表達式。 |
it filterNot p | 傳回一個疊代器,指向it所指元素中不滿足條件p的元素。 |
拆分(Subdivision): | |
it partition p | 将it分為兩個疊代器;一個指向it所指元素中滿足條件謂詞p的元素,另一個指向不滿足條件謂詞p的元素。 |
條件元素(Element Conditions): | |
it forall p | 傳回一個布爾值,指明it所指元素是否都滿足p。 |
it exists p | 傳回一個布爾值,指明it所指元素中是否存在滿足p的元素。 |
it count p | 傳回it所指元素中滿足條件謂詞p的元素總數。 |
折疊(Fold): | |
(z /: it)(op) | 自左向右在it所指元素的相鄰元素間應用二進制操作op,初始值為z。 |
(it :\ z)(op) | 自右向左在it所指元素的相鄰元素間應用二進制操作op,初始值為z。 |
it.foldLeft(z)(op) | 與(z /: it)(op)相同。 |
it.foldRight(z)(op) | 與(it :\ z)(op)相同。 |
it reduceLeft op | 自左向右對非空疊代器it所指元素的相鄰元素間應用二進制操作op。 |
it reduceRight op | 自右向左對非空疊代器it所指元素的相鄰元素間應用二進制操作op。 |
特殊折疊(Specific Fold): | |
it.sum | 傳回疊代器it所指數值型元素的和。 |
it.product | 傳回疊代器it所指數值型元素的積。 |
it.min | 傳回疊代器it所指元素中最小的元素。 |
it.max | 傳回疊代器it所指元素中最大的元素。 |
拉鍊方法(Zippers): | |
it zip jt | 傳回一個新疊代器,指向分别由it和jt所指元素一一對應而成的二進制組序列。 |
it zipAll (jt, x, y) | 傳回一個新疊代器,指向分别由it和jt所指元素一一對應而成的二進制組序列,長度較短的疊代器會被追加元素x或y,以比對較長的疊代器。 |
it.zipWithIndex | 傳回一個疊代器,指向由it中的元素及其下标共同構成的二進制組序列。 |
更新: | |
it patch (i, jt, r) | 由it傳回一個新疊代器,其中自第i個元素開始的r個元素被疊代器jt所指元素替換。 |
比對: | |
it sameElements jt | 判斷疊代器it和jt是否依次傳回相同元素注意:it和jt中至少有一個會步進到終點。 |
字元串(String): | |
it addString (b, start, sep, end) | 添加一個字元串到StringBuilder b,該字元串以start為字首、以end為字尾,中間是以sep分隔的it所指向的所有元素。start、end和sep都是可選項。 |
it mkString (start, sep, end) | 将it所指所有元素轉換成以start為字首、end為字尾、按sep分隔的字元串。start、sep、end都是可選項。 |
帶緩沖的疊代器
有時候你可能需要一個支援“預覽”功能的疊代器,這樣我們既可以看到下一個待傳回的元素,又不會令疊代器跨過這個元素。比如有這樣一個任務,把疊代器所指元素中的非空元素轉化成字元串。你可能會這樣寫:
def skipEmptyWordsNOT(it: Iterator[String]) = while (it.next().isEmpty) {}
但仔細看看這段代碼,就會發現明顯的錯誤:代碼确實會跳過空字元串,但同時它也跳過了第一個非空字元串!
要解決這個問題,可以使用帶緩沖能力的疊代器。[BufferedIterator]類是[Iterator]的子類,提供了一個附加的方法,head。在BufferedIterator中調用head 會傳回它指向的第一個元素,但是不會令疊代器步進。使用BufferedIterator,跳過空字元串的方法可以寫成下面這樣:
def skipEmptyWords(it: BufferedIterator[String]) = while (it.head.isEmpty) { it.next() }
通過調用buffered方法,所有疊代器都可以轉換成BufferedIterator。參見下例:
scala> val it = Iterator(1, 2, 3, 4)it: Iterator[Int] = non-empty iteratorscala> val bit = it.bufferedbit: java.lang.Object with scala.collection. BufferedIterator[Int] = non-empty iteratorscala> bit.headres10: Int = 1scala> bit.next()res11: Int = 1scala> bit.next()res11: Int = 2