你正在使用一個巨大的集合,而且想建立一個懶加載的版本。只有在計算或者返回結果時才真正被調用。java
除了Stream類,不論何時你建立一個Scala集合類的實例,你都建立了一個strict版本集合(任何操做都會被當即執行)。這意味着若是你新建了一個百萬元素的集合,這些元素會當即加載進內存。在Java中這是正常的,可是在Scala中你能夠選擇在集合上新建一個視圖。視圖可讓結果nonstrict,或者懶加載。這改變告終果集合,因此當調用集合的轉換方法的時候,只有真正要訪問集合元素的時候纔會執行計算,而且不像平時那樣是「當即執行」。(轉換方法是把一個輸入集合轉化爲一個輸出集合。)es6
你能夠看下建立集合的時候使用view與不使用view的區別:算法
scala> val nums = 1 to 100 nums: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100) scala> val numsView = (1 to 100).view numsView: scala.collection.SeqView[Int,scala.collection.immutable.IndexedSeq[Int]] = SeqView(...)
不使用view建立一個Range就像你指望的結果同樣,一個100個元素的Range。然而,使用view的Range在REPL中出現了不一樣的輸出結果,一個叫作SeqView的東西。
數據庫
這個SeqView帶有以下信息:
數組
集合元素類型爲Int性能
輸出結果scala.collection.immutable.IndexedSeq[Int],暗示了你使用force方法把view轉回正常集合時候你能獲得的集合元素類型。spa
你會看到下面的信息,若是你強制把一個view轉回一個普通集合:scala
scala> val numsView = (1 to 100).view numsView: scala.collection.SeqView[Int,scala.collection.immutable.IndexedSeq[Int]] = SeqView(...) scala> val x = numsView.force x: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)
存在許多中方法能看到使用一個集合view的效果。首先,咱們來看一看foreach方法,它好像沒什麼區別。代理
scala> (1 to 100).foreach(x => print(x + " ")) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 scala> (1 to 100).view.foreach(x => print(x + " ")) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
這兩個例子都會直接打印出集合的100個元素,由於foreach方法並非一個轉換方法,因此對結果沒有影響。
code
可是當你調用一個轉換方法的時候,你會戲劇性的發現結果變得不一樣了:
scala> (1 to 10).map(_ * 2) res61: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) scala> (1 to 10).view.map(_ * 2) res62: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...)
結果不一樣了,應爲map是一個轉換方法。咱們來使用下面的代碼來更深層次的展現一下這種不一樣:
scala> (1 to 10).map{x => { | Thread.sleep(1000) | x * 2 | }} res68: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) scala> (1 to 10).view.map{x => { | Thread.sleep(1000) | x * 2 | }} res69: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...)
不是用view的時候,程序會等待10秒,而後直接返回結果。使用view,程序則直接返回scala.collection.SeqView。
Scala文檔對view作出了一個說明:「僅僅對集合的結果構造了代理,它的元素構件只有一個要求...一個view是一個特殊類型的集合,它實現了集合的一些基本方法,可是對全部的transformers實現了懶加載」
一個transformer方法是可以從一個原有集合構造一個新的集合。這樣的方法包括map,filter,reverse等等。當你使用這些方法的時候,你就在把一個輸入集合轉化爲一個輸出集合。
這就解釋了爲何foreach方法在使用view和沒有使用view時沒有任何區別:它不是一個transformer方法。可是map方法和其餘transformer方法好比reverse,就能夠有懶加載的效果:
scala> val l = List(1,2,3) l: List[Int] = List(1, 2, 3) scala> l.view.reverse res70: scala.collection.SeqView[Int,List[Int]] = SeqViewR(...)
對於view,有兩個主要的使用場景:
性能
像處理數據庫視圖同樣處理集合
關於性能,駕駛你遇到一種狀況,不得不處理一個十億元素的集合。若是你不得不作的話,你確定不但願直接在10億元素上運行一個算法,因此這時候使用一個視圖是有意義的。
第二個應用場景讓你使用Scala view就像使用一個數據庫view同樣。下面這段代碼展現瞭如何把一個scala集合view看成一個數據庫view使用:
scala> val arr = (1 to 10).toArray arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> val view = arr.view.slice(2, 5) view: scala.collection.mutable.IndexedSeqView[Int,Array[Int]] = SeqViewS(...) scala> arr(2) = 42 scala> view.foreach(println) 42 4 5 scala> view(0) = 10 scala> view(1) = 20 scala> view(2) = 30 scala> arr res76: Array[Int] = Array(1, 2, 10, 20, 30, 6, 7, 8, 9, 10)
改變數組中元素的值會改變view,改變view中對應數據元素的值一樣會改變數組元素值。當你想要修改一個集合子集的元素時,給集合建立一個view而後修改對應的元素是一個很是好的方法來實現這個目標。
最後須要注意的是,不要錯誤的認爲使用view能夠節省內存。下面這兩個行爲會拋出一個「java.lang.OutOfMemoryError:Java heap space」錯誤信息:
scala> val a = Array.range(0,123456789) java.lang.OutOfMemoryError: Java heap space scala> val a = Array.range(0,123456789).view java.lang.OutOfMemoryError: Java heap space
最後說一句,視圖就是推遲執行,該用多大內存還使用多大內存,該遍歷多少元素仍是遍歷多少元素。說白了scala視圖就跟數據庫視圖同樣,不使用視圖就跟數據庫創建臨時表同樣。使用視圖,當原始集合改變的時候,不須要從新跑transformers方法,使用視圖則每次使用視圖的時候都會跑一次transformers方法內容。
scala> def compare(x: Int): Boolean = { | println(s"compare $x and 5") | return x < 5 | } compare: (x: Int)Boolean scala> val l = List(1,2,3,4,5,6,7,8,9).view.filter(x => compare(x)) l: scala.collection.SeqView[Int,List[Int]] = SeqViewF(...) scala> l.map(_ * 2) res80: scala.collection.SeqView[Int,Seq[_]] = SeqViewFM(...) scala> l.map(_ * 2).force compare 1 and 5 compare 2 and 5 compare 3 and 5 compare 4 and 5 compare 5 and 5 compare 6 and 5 compare 7 and 5 compare 8 and 5 compare 9 and 5 res82: Seq[Int] = List(2, 4, 6, 8)