02-Scala複雜型別入門
Scala Programming程式設計指南
汪文君
目錄
寫在最開始的話
其實在很久之前,我有自己學習過一些基於JVM的語言,包括Scala,Groovy,Bshell(不是linux中的shell,是最早的Sun公司出的bshell),但是在工作和日常生活中用不到之後,學習了又會忘記,不知道是不是記憶力不好的原因,但是此次公司決定使用Spark,準確的說是明年第二季度開始使用,所以我又開始整理關於Scala的知識,希望藉著這個機會能夠將Scala的語言特點整理,並且熟練掌握,應用於工作和日常開發之中。
關於Scala這門語言,其實語法量比Java多很多,很多老外建議我使用Clojure,不要使用Scala,他們給出最大的一個槽點就是語法太多,用他們的話講就是完成一件事,scala有太多的方式可供選擇,這樣反倒不太好,但是不管怎樣,既然要去使用這麼語言來餬口養家,那麼就自動遮蔽一些否定的聲音,也許他們說的有道理。
另外一個很重要的話題是,如果你掌握了Java 8,其中一些函數語言程式設計對知識,對學習Scala的幫助非常大,本人利用業餘的時間錄製了一套關於Java 8的視訊教程,大概有40集,全部是針對Java 8新語法和新程式設計思想的實戰,感興趣的可以去下載觀看
2、Scala複雜型別入門
2.1 Scala中的陣列
在上一個電子書中,大致學習了一下如何定義變數,定義函式,以及使用scala指令碼,簡單介紹了一下while迴圈和foreach迴圈,我們在這一節課中我們來使用一些稍微複雜一點的Scala資料型別,就先從Array入手吧
2.1.1 Scala陣列介紹
關於Scala中的資料其實和很多語言中的資料定義完全一樣,說白了就是一個固定空間裡存放著一些型別一樣的資料,比如我們定義一個數組型別為String
val greetingString: Array[String] = new Array[String](3) |
當然其中的資料型別也是可以省略的
val greetingString = new Array[String](3) |
我們來看看如何給陣列中的元素賦值以及獲取元素的值
greetingString(0)="Hello" greetingString(1)="World" greetingString(2)="Scala" println (greetingString(0)) println (greetingString(1)) println (greetingString(2)) |
通過for迴圈的方式對其進行輸出
for (i <- 0 to(greetingString.length - 1)) println(greetingString(i)) |
2.1.2 萬物皆物件
上面的例子中,看到紅色加粗的字型了麼,寫法有些奇怪,其實to是數字0的一個方法,數字也有方法?沒錯,在Scala中其實沒有什麼數字操作符可言,更沒有Java中的所謂基本資料型別可言,所有的物件都是物件,0就是一個物件,+-/×這些都不是運算子,而是數字物件的方法,如果單純這一點上來說,scala面向物件的更為徹底,那麼我們再來看看既然是物件,為什麼0後面沒有物件導航符號“.”這是Scala的語言特性,在Scala中如果一個方法只有一個引數,所在物件對其的呼叫可以省略. 甚至引數的括號,我們來看看這個例子
object ArrayInAction { def main(args: Array[String]): Unit = { ArrayInAction.echo("Hello Scala") ArrayInAction echo "Hello Scala" } def echo(msg: String): Unit = { println(msg) } } |
2.1.3 再談Array元素的存取
在2.2.1中我們看到,獲取的方式是通過(),這一點和Java還是有區別的,這在Scala中其實也是一種簡寫的方法,是由Array的伴生物件提供的,我們來看看,完整的元素存取到底是怎麼樣的
val greetingString: Array[String] = new Array[String](3) greetingString.update(0, "Hello") greetingString.update(1, "World") greetingString.update(2, "Scala") for (i <- 0 to (greetingString.length - 1)) println(greetingString.apply(i)) |
同步視訊:
2.2 Scala中的List
和Array相比,List是可變長的,這是Java中的關於List的印象,可是在Scala中,List不僅不可變,裡面的元素都不能變
2.2.1 List基本用法
val list1: List[Int] = List(1, 2, 3) |
我們定義了一個List,但是這個List不像我們想象的那樣可變長,裡面的元素是可以變化的,對其每一次的操作都會產生一個新的List
val list2: List[Int] = list1.updated(1, 100) |
改變了其中的一個元素,就會產生一個新的list
List之間的疊加:::,類似於addAll,但是仍然會產生一個新的List
val list3: List[Int] = List(1, 2, 3) val list4: List[Int] = List(4, 5, 6) val joinedList: List[Int] = list3 ::: list4 |
List append,有點類似我們給Java中的List add元素,但是他是右操作,看看下面的例子
val list5: List[Int] = 8 :: list3 |
根據我們前面的知識,是不是認為::是數字物件的方法呢?其實不然,這是List中的一個有操作符號,和下面的等價
val list6: List[Int] = list3.::(8) |
在List中還有一個非常重要物件Nil,他不等同於Lua語言中的nil,不代表NULL的意思,而代表一個空的List.
同步教學視訊:
2.2.2 List的更多用法
List提供的方法比較豐富,相對比Java中的list,方法比較多,我們在這裡大概說一下其中的一些用法
上面的方法,我們不會一一介紹,簡單的說幾個就可以了
l Count
val count: Int = list.count(i => i > 4) |
統計符合條件的元素個數
l Drop
去掉List中前兩位的元素
val list2 = list.drop(2) |
l dropRight
去掉最後面的兩個元素
val list2 = list.dropRight(2) |
l Exists
List中是否存在一個特定的元素
val exists: Boolean = list.exists(i => i == 8) |
l Filter
獲取符合條件的子集
val list2: List[Int] = list.filter(i => i % 2 == 0) |
l Forall
判斷List中的所有元素是否滿足條件
val forall: Boolean = list.forall(i => i > 2) |
l Map
對List中所有的元素都進行某種操作,產生一個新的,比如想讓所有的元素都增大10倍
val map: List[Int] = l4.map(i => i * 10) |
2.2.3 同Java 8 的對比
關於List中的很多用法,其實用Java 8 Stream也可以實現類似的函式式效果,我們來看看如何使用Java 8 的stream結合lambda簡潔有效的解決
package com.wangwenjun.chapter2; import java.util.Arrays; import java.util.List; import java.util.Optional; import static java.util.stream.Collectors.toList; /*************************************** * @author:Alex Wang * @Date:2016/11/23 QQ:532500648 * QQ交流群:286081824 ***************************************/ public class ScalaListFunctionByJava { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 5, 6, 7); long count = list.stream().filter(i -> i > 4).unordered().count(); Optional.of(count).ifPresent(System.out::println); Optional.ofNullable(list.stream().map(i -> i * 10).collect(toList())) .ifPresent(System.out::println); } } |
本節同步視訊地址:
2.3 Scala中的Tuple
Tuple也是類似於List的容器,元組也是不可變的,但是它不同於List是因為它可以同時存放多型別的元素
package com.wangwenjun.chapter2 /** ************************************* * * @author:Alex Wang * @Date:2016 /11/26 * QQ:532500648 * QQ交流群:286081824 ***************************************/ object TupleInAction extends App { val t1 = (123, 'X', "Hello"); println(t1._1) println(t1._2) println(t1._3) } |
我們簡單定義了一個Tuple,分別存放了數字,字元,字串型別,並且我們通過t1._1等方式對其進行了訪問,在Tuple中沒有apply方法,只能通過下標的方式來獲取,那是因為,apply方法始終返回同類型的資料,但是很明顯Tuple中的元素不具備這樣的條件,更重要的一點是tuple中的元素下標開始是基於1而不是0.
2.4 Scala中的Set和Map
在Scala中也存在類似Java的Set和Map,但是他卻有兩種不同的實現,可變與不可變,基於入門的考慮,我們會在後面詳細介紹Set和Map的用法,在這裡只是暫時說說其基本用法
2.4.1 Scala中的Set
package com.wangwenjun.chapter2 /** ************************************* * * @author:Alex Wang * @Date:2016 /11/26 * QQ:532500648 * QQ交流群:286081824 ***************************************/ object SetInAction extends App { var set: Set[String] = Set("Hello", "World") println(set) println(set hashCode) set.+=("Scala") set += "Scala" println(set) println(set hashCode) println("=========================") set.foreach(println) } |
Set中的元素是不可重複的,我們用預設的方式建立Set,產生的是一個不可變的Set,上面的這段程式碼只是簡單的介紹了一下如何建立Set,如何給Set新增一個元素,關於更加深入的Set知識,我們在後面會詳細說明
2.4.2 Scala中的Map
關於Map也是Key,Value的形式,在Scala中同樣也有Map,只不過更加的豐富,同樣,我們也會在後面專門的章節中詳細介紹
package com.wangwenjun.chapter2 import scala.collection.mutable.Map /** ************************************* * * @author:Alex Wang * @Date:2016 /11/26 * QQ:532500648 * QQ交流群:286081824 ***************************************/ object MapInAction extends App { import scala.collection.mutable.Map val map: Map[Int, String] = Map[Int, String](); map.+=(1 -> "Alex") map.+=(2 -> "WenJun") map.+=(3 -> "Scala") map.+=(4 -> "Java") val map2: Map[Int, String] = Map( 1 -> "Alex", 2 -> "Scala", 3 -> "Java", 4 -> "Collection" ) map2.foreach(item => println(item._1 + "," + item._2+" "+item.getClass)) } |
2.5 函式式風格初識
我們還是拿迴圈輸出作為函式式的演變,看看下面的程式碼
package com.wangwenjun.chapter2 /**************************************** * * @author:Alex Wang * @Date:2016 /11/26 * QQ:532500648 * QQ交流群:286081824 ***************************************/ object FunctionInAction { def main(args: Array[String]): Unit = { val array: Array[String] = Array("Hello", "World", "Scala", "Java") printArray(array) printArrayForEach(array) printArrayFunction(array) val map: Map[Int, String] = Map[Int, String]( 1 -> "Hello", 2 -> "World", 3 -> "Scala", 4 -> "Java" ) map.foreach(printMap) } def printMap(item: Tuple2[Int,String]) = println(item._1 + " <=> " + item._2) def printArray(array: Array[String]): Unit = { for (v <- array) println(v) } def printArrayForEach(array: Array[String]): Unit = { array.foreach(println) } def printArrayFunction(array: Array[String]) = array.foreach(println); } |
上面的例子我們從printArray一直到printArrayFunction越來越函式化的演變,並且對Map的foreach進行了函式式的改進。
2.6 Scala和檔案
在本小節中,我們簡單的介紹一下Source型別,知道如何通過source獲取檔案的內容,關於更多檔案操作的內容我們會在後面的章節中專門介紹
package com.wangwenjun.chapter2 package com.wangwenjun.chapter2 import java.io.File import scala.io.Source /** ************************************* * * @author:Alex Wang * @Date:2016 /11/26 * QQ:532500648 * QQ交流群:286081824 ***************************************/ object SourceInAction extends App { val file: File = new File("C:\\Users\\wangwenjun\\IdeaProjects\\scalaInAction\\src\\com\\wangwenjun\\chapter2\\SourceInAction.scala"); val source: Source = Source.fromFile(file) val content: Iterator[String] = source.getLines for (line <- content) println(line) source.close() } |
寫在最後的話
本節課的內容大概就這麼多,我們會逐步的更新,為了能夠讓讀者儘快看到最新的內容,沒更新一次就會發布一次,不用等到搞定一切之後再做統一發布,有問題可以和我交流,我們一起共同學習,共同進步,期待著你們的建議。