天天看點

[scala基礎]--iterators類操作

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 iterator​​​​scala> it.map(_.length)​​​​res1: Iterator[Int] = non-empty iterator​​​​scala> res1 foreach println​​​​1​​​​6​​​​2​​​​5​​​​scala> 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 iterator​​​​scala> it dropWhile (_.length < 2)​​​​res4: Iterator[java.lang.String] = non-empty iterator​​​​scala> 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 iterator​​​​scala> val bit = it.buffered​​​​bit: java.lang.Object with scala.collection.​​​​ BufferedIterator[Int] = non-empty iterator​​​​scala> bit.head​​​​res10: Int = 1​​​​scala> bit.next()​​​​res11: Int = 1​​​​scala> bit.next()​​​​res11: Int = 2​​