Scala學習(四)---對映和元組

sunddenly發表於2015-06-27

對映和元組

摘要:

一個經典的程式設計師名言是:"如果只能有一種資料結構,那就用雜湊表吧"。雜湊表或者更籠統地說對映,是最靈活多變的資料結構之一。對映鍵/值對偶的集合。Scala有一個通用的叫法:元組,即n個物件的聚集,並不一定要相同型別的。對偶不過是一個 n=2的元組,元組對於那種需要將兩個或更多值聚集在一起時特別有用。本篇的要點包括:

01. Scala有十分易用的語法來建立、查詢和遍歷對映。

02. 你需要從可變的和不可變的對映中做出選擇。

03. 預設情況下,你得到的是一個雜湊對映,不過你也可以指明要樹形對映。

04. 你可以很容易地在Scala對映和Java對映之間來回切換。

05. 元組可以用來聚集值。

構造對映

不可變對映

我們可以這樣構造一個對映:

val scores = Map ("Alice"-> 10, "Bob"->3, "Cindy"->8)

上述程式碼構造出一個不可變的Map[String,Int],其值不能被改變

可變對映

如果你想要一個可變對映,則用

val scores = scala.collection.mutable.Map("Alice"-> 10, "Bob"->3, "Cindy"->8)

如果想從—個空的對映開始,你需要選定一個對映實現並給出型別引數

val scores = new scala.collection.mutable.HashMap [String, Int]

在Scala中,對映是對偶的集合。對偶簡單地說就是兩個值構成的組,這兩個值並不一定是同一個型別的,比如("Alice",10)

操作符建立對偶

操作符用來建立對偶:

"Alice"->10

上述程式碼產出的值是:

("Alice", 10)

完全也可以用下面這種方式來定義對映:

val scores=Map ( ("Alice", 10), ("Bob", 3), ("Cindy", 8) )

只不過->操作符看上去比圓括號更易讀那麼一點,也更加符合大家對對映的直觀感覺:對映這種資料結構是一種將對映到函式。區別在於通常的函式計算值,而對映只是做查詢

獲取對映中的值

在Scala中,函式對映之間的相似性尤為明顯,因為你將使用()表示法來查詢某個鍵對應的值

val bobsScore = scores ("Bob") //類似於Java中的scores.get ("Bob")

如果對映並不包含請求中使用的鍵,則會丟擲異常。要檢查對映中是否有某個指定的鍵,可以用contains方法:

val bobsScore = if (scores.contains ("Bob")) scores("Bob") else 0

由於這樣的組合呼叫十分普遍,以下是一個快捷寫法:

val bobsScore = scores.getOrElse("Bob", 0) // 如果對映包含鍵"Bob",返回對應的值;否則,返回0

最後,對映.get(鍵)這樣的呼叫返回一個Option物件,要麼是Some(鍵對應的值),要麼是None

更新鍵值

更新可變對映

在可變對映中,你可以更新某個對映的值,或者新增一個新的對映關係,做法是在=號的左側使用()

scores ("Bob") = 10 // 更新鍵"Bob"對應的值

scores ("Fred")=7 // 增加新的鍵/值對偶到scores

或者,你也可以用+=操作來新增多個關係

scores+= ("Bob"-> 10, "Fred"->7)

移除某個鍵和對應的值,使用-=操作符

scores -= "Alice"

不可變對映

雖然不能更新一個不可變的對映,但你可以做一些同樣有用的操作,即獲取一個包含所需要的更新的新對映:

val newScores = scores+ ("Bob"->10,"Fred"->7) // 更新過的新對映

newScores對映包含了與scores相同的對映關係,此外"Bob"被更新,"Fred"被新增了進來

除了把結果作為新值儲存外,你也可以更新Var變數:

var scores = …

scores = scores+ ("Bob"->10, "Fred"->7)

同理,要從不可變對映中移除某個鍵,你可以用一操作符來獲取一個新的去掉該鍵的對映:

scores = scores - "Alice"

你可能會覺得這樣不停地建立新對映效率很低,不過事實並非如此。老的和新的對映共享大部分結構。這樣做之所以可行,是因為它們是不可變的。

迭代對映

如下這段超簡單的迴圈即可遍歷對映中所有的鍵/值對偶:

for ((k,v) <- 對映) 處理k和v

這裡的"魔法"是你可以在Scala的for迴圈中使用模式匹配。這樣一來,不需要冗雜的方法呼叫,你就可以得到每一個對偶的鍵和值

如果出於某種原因,你只需要訪問鍵或值,像Java一樣,則可以用keySetvalues方法。values方法返回—個Iterable,你可以在for迴圈當中使用這個Iterable

scores.keySet // 一個類似Set("Bob","Cindy","Fred","Alice")這樣的集合

for ( v <- scores.values) printlnI(v) // 將列印10 8 7 10或其他排列組合

要反轉一個對映,即交換鍵和值的位置,可以用:

for ( (k,v) <- 對映) yield (v.k)

已排序對映

在操作對映時,你需要選定一個實現:一個雜湊表或者一個平衡樹。預設情況下,Scala給的是雜湊表。由於對使用的鍵沒有很好的雜湊函式,或者需要順序地訪問所有的鍵,因此,你可能會想要一個樹形對映。

要得到一個不可變的樹形對映而不是雜湊對映的話,可以用:

val scores = scala.collections.immutable.SortedMap("Alice"->10,"Fred"->7, "Bob"->3,"Cindy"->8)

很可惜,Scala( 2.9)並沒有可變的樹形對映。如果那是你要的,最接近的選擇是使用Java的TreeMap。需要注意的是:如果要按插入順序訪問所有鍵,使用LinkedHashMap,例如:

val months = scala.collection.mutable.LinkedHashMap("January" -> 1,"February" -> 2, "March" -> 3, …)

與Java互操作

Java到Scala

如果你通過Java方法呼叫得到了一個Java對映,你可能想要把它轉換成一個Scala對映,以便使用更便捷的Scala對映API。這對於需要操作Scala,並未提供的可變樹形對映的情況也很有用。只需要增加如下引入語句:

import scala.collection.JavaConversions .mapAsScalaMap

然後通過指定Scala對映類型來觸發轉換

val scores:scala.collection.mutable.Map[String, Int]=new java.util.TreeMap[String, Int]

除此之外,你還可以得到從java.util.PropertiesMap[String,String]的轉換:

import scala.collection.JavaConversions.propertiesAsScalaMap

val props:Scala.collection.Map[String, String] = System.getProperties()

Scala到Java

反過來講,要把Scala對映傳遞給預期Java對映的方法,提供相反的隱式轉換即可

例如:

import scala.collection.JavaConversions.mapAsjavaMap

import java.awt.font.TextAttribute. _ // 引入下面的對映會用到的鍵

val attrs = Map ( FAMILY -> "Serif", SIZE -> 12 ) // Scala對映

val font=new java.awt.Font (attrs) // 該方法預期一個Java對映

元組

元組定義

對映是鍵/值對偶的集合。對偶足元組( tuple)的最簡單形態,元組是不同型別的值的聚集元組的值是通過將單個的值包含在圓括號中構成的。例如:

(1, 3.14, "Fred")

是一個元組,型別為:

Tuple3 [Int, Double, java.lang.String]

型別定義也可以寫為:

(Int, Double, java,lang.String)

如果你有一個元組,比如:

val t = (1,3.14, "Fred")

你就可以用方法_1、_2、_3訪問其組元,比如:

val second = t._2 // 將second設為3.14

需要注意的是:和陣列或字串中的位置不同,元組的各組元從1開始,而不是0。你可以把t._2寫為t _2,即用空格而不是句點,但不能寫成t_2

獲取元組

通常,使用模式匹配來獲取元組的組元,例如:

val (first,second,third)=t //將first設為1,second設為3.14.third設為"Fred"

如果並不是所有的部件都需要,那麼可以在不需要的部件位置上使用_

val (first, second, _ ) = t

元組可以用於函式需要返回不止一個值的情況。舉例來說,StringOps的partition方法返回的是一對字串,分別包含了滿足某個條件和不滿足該條件的字元:

"New York".partition ( _.isUpper) //輸出對偶( "NY","ew ork")

拉鍊操作

使用元組的原因之一是把多個值綁在一起,以便它們能夠被一起處理,這通常可以用zip方法來完成。舉例來說,下面的程式碼:

val symbols = Array ("<","-",">")

val counts = Array (2, 10, 2)

val pairs = symbols.zip(counts)

輸出對偶的陣列:

Array ( ("<",2),("-",10), (">",2) )

然後這些對偶就可以被一起處理:

for ( (s,n) <- pairs ) Console.print(s*n) // 會列印<<---------->>

需要注意的是:用toMap方法可以將對偶的集合轉換成對映。如果你有一個鍵的集合,以及一個與之平行對應的值的集合,那麼你就可以用拉鍊操作將它們組合成一個對映:keys.zip(values) .toMap

如果,您認為閱讀這篇部落格讓您有些收穫,不妨點選一下右下角的【推薦】。
如果,您希望更容易地發現我的新部落格,不妨點選一下左下角的【關注我】。
如果,您對我的部落格所講述的內容有興趣,請繼續關注我的後續部落格,我是【Sunddenly】。

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。

相關文章