十個(gè)驚人的Scala集合操作函數(shù)
當(dāng)我操作 Scala 集合時(shí),我一般會進(jìn)行兩類操作:轉(zhuǎn)換操作(transformation )和行動操作(actions)(有些人喜歡叫他為聚合操作)。第一種操作類型將集合轉(zhuǎn)換為另一個(gè)集合,第二種操作類型返回某些類型的值。
本文我將集中介紹幾個(gè)日常工作必備的 [Scala(https://www.iteblog.com/archives/tag/scala/) 集合函數(shù),如轉(zhuǎn)換函數(shù)和聚合函數(shù)。文章最后,我會展示如何結(jié)合這些函數(shù)以解決具體問題。
文章目錄
· 1?最大值和最小值
· 2?Filter
· 4?歐拉圖函數(shù)(Euler Diagram函數(shù))
· 9?Fold
十個(gè)驚人的scala集合操作函數(shù)
https://www.iteblog.com/archives/1946.html
最大值和最小值
我們先從動作函數(shù)(
action function)開始。在序列中查找最大或最小值是一個(gè)極常見的需求,較常用于面試問題和算法。還記得 Java 中的代碼行嗎?如下:
int[] arr = {11, 2, 5, 1, 6, 3, 9};
int to = arr.length - 1;
int max = arr[0];
for (int i = 0; i < to; i++) {
if (max < arr[i+1])
max = arr[i+1];
}
System.out.println(max);
問題:怎么在List 中找到最大/最小值呢?
Scala 推薦了一個(gè)很贊的解決方案:
val numbers = Seq(11, 2, 5, 1, 6, 3, 9)
numbers.max //11
numbers.min //1
但實(shí)際操作的數(shù)據(jù)更加復(fù)雜。下面我們介紹一個(gè)更高級的例子,其中包含一個(gè)書的序列(查看源代碼案例)。
case class Book(title: String, pages: Int)
val books = Seq(
Book("Future of Scala developers", 85),
Book("Parallel algorithms", 240),
Book("Object Oriented Programming", 130),
Book("Mobile Development", 495)
)
//Book(Mobile Development,495)
books.maxBy(book => book.pages)
//Book(Future of Scala developers,85)
books.minBy(book => book.pages)
如上所示,
minBy & maxBy方法解決了復(fù)雜數(shù)據(jù)的問題。你只需選擇決定數(shù)據(jù)最大或最小的屬性。
Filter
你過濾過集合嗎?比如,篩選價(jià)格大于10美元的條目,或挑選年齡在24歲以下員工等,所有這些操作屬于過濾。
讓我們舉例說明:過濾一個(gè)數(shù)字List,只獲取奇數(shù)的元素。
val numbers = Seq(1,2,3,4,5,6,7,8,9,10)
numbers.filter(n **=**> n **%** 2 **==** 0)
然后加大難度,我想獲取頁數(shù)大于
120頁的書。
val books = Seq(
Book("Future of Scala developers", 85),
Book("Parallel algorithms", 240),
Book("Object Oriented Programming", 130),
Book("Mobile Development", 495)
)
books.filter(book => book.pages >= 120)
實(shí)際上,過濾是一個(gè)轉(zhuǎn)換類型的方法,但是比運(yùn)用min和max方法簡單。
還有一個(gè)與filter類似的方法是filterNot。它的名字就體現(xiàn)了它的作用。如果你還是不了解它的實(shí)際用途,你可以在一個(gè)示例中,用filterNot替換filter 方法。
Flatten
我想大多數(shù)朋友都沒聽說過這個(gè)功能。其實(shí)它很好理解,我們來舉例說明:
val abcd = Seq('a', 'b', 'c', 'd')
val efgj = Seq('e', 'f', 'g', 'h')
val ijkl = Seq('i', 'j', 'k', 'l')
val mnop = Seq('m', 'n', 'o', 'p')
val qrst = Seq('q', 'r', 's', 't')
val uvwx = Seq('u', 'v', 'w', 'x')
val yz = Seq('y', 'z')
val alphabet = Seq(abcd, efgj, ijkl, mnop, qrst, uvwx, yz)
// List(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)
alphabet.flatten
當(dāng)有一個(gè)集合的集合,然后你想對這些集合的所有元素進(jìn)行操作時(shí),就會用到
flatten。
歐拉圖函數(shù)(Euler Diagram函數(shù))
不要緊張!接下來的操作大家都熟知:差集、交集和并集。以下示例能很好地解釋
Euler Diagram 函數(shù):
val num1 = Seq(1, 2, 3, 4, 5, 6)
val num2 = Seq(4, 5, 6, 7, 8, 9)
//List(1, 2, 3)
num1.diff(num2)
//List(4, 5, 6)
num1.intersect(num2)
//List(1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9)
num1.union(num2)
上述示例中的 union保留了重復(fù)的元素。如果我們不需要重復(fù)怎么辦?這時(shí)可以使用
distinct函數(shù):
//List(1, 2, 3, 4, 5, 6, 7, 8, 9)
num1.union(num2).distinct
下面是上述功能的圖示:
map列表元素
map 是 Scala 集合最常用的一個(gè)函數(shù)。它的功能十分強(qiáng)大:
val numbers = Seq(1,2,3,4,5,6)
//List(2, 4, 6, 8, 10, 12)
numbers.map(n **=**> n * 2)
val chars = Seq('a', 'b', 'c', 'd')
//List(A, B, C, D)
chars.map(ch **=**> ch.toUpper)
map 函數(shù)的邏輯是遍歷集合中的元素并對每個(gè)元素調(diào)用函數(shù)。你也可以不調(diào)用任何函數(shù),保持返回元素本身,但這樣 map無法發(fā)揮作用,因?yàn)槟阍谟成溥^后得到的是同樣的集合。
flatMap
我很難具體說明flatMap 的使用場合,因?yàn)楹芏嗖煌那闆r下都會用到 flatMap。如果大家仔細(xì)觀察,就會發(fā)現(xiàn)flatMap 是由下列這兩個(gè)函數(shù)組成的:map & flatten
現(xiàn)在,假設(shè)我們想知道字母表中的大寫字母和小寫字母的排列情況:
val abcd = Seq('a', 'b', 'c', 'd')
//List(A, a, B, b, C, c, D, d)
abcd.flatMap(ch **=**> List(ch.toUpper, ch))
因?yàn)檫@篇文章是關(guān)于集合功能的介紹,所以此處略過
Future 和 Option 的示例。
對整個(gè)集合進(jìn)行條件檢查
有一個(gè)場景大家都知道,即確保集合中所有元素都要符合某些要求,如果有哪怕一個(gè)元素不符合條件,就需要進(jìn)行一些處理:
val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)
//ture
numbers.forall(n **=**> n < 10)
//false
numbers.forall(n **=**> n > 5)
而forall 函數(shù)就是為處理這類需求而創(chuàng)建的。
對集合進(jìn)行分組
你是否嘗試過將一個(gè)集合按一定的規(guī)則拆分成兩個(gè)新的集合?比如,我們把某個(gè)集合拆分成偶數(shù)集和奇數(shù)集,partition 函數(shù)可以幫我們做到這一點(diǎn):
val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)
//(List(2, 6, 4, 2), List(3, 7, 9, 5, 1))
numbers.partition(n **=**> n **%** 2 **==** 0)
Fold
另一個(gè)流行的操作是fold。
在Scala 的上下文中,通常可以考慮 foldLeft 和 foldRight。他們是從不同的方面做同樣的工作:
val numbers = Seq(1, 2, 3, 4, 5)
//15
numbers.foldLeft(0)((res, n) **=**> res + n)
在第一對括號中,我們放一個(gè)起始值。
在第二對括號中,我們定義需要對數(shù)字序列的每個(gè)元素執(zhí)行的操作。
第一步,n = 0,然后它根據(jù)序列元素變化。
另一個(gè)關(guān)于foldLeft 的例子,計(jì)算字符數(shù):
val words = Seq("apple", "dog", "table")
//13
words.foldLeft(0)((resultLength, word) **=**> resultLength + word.length)
您最喜歡的函數(shù)
經(jīng)過了上面一系列的列舉,從Scala集合找到你最喜歡的函數(shù)是很酷的(cool)。請大家在評論中寫下它,并提供其使用的例子。
最近我通過了一個(gè)編譯測試,任務(wù)的內(nèi)容是:給你一個(gè)String S,你需要找到包含大寫和小寫字符,但不包含數(shù)字的最長子字符串。
比如: dP4knqw1QAp
答案: QAp
那么我們?nèi)绾问褂肧cala集合函數(shù)來解決這個(gè)問題呢:
def theLongest(s: String): String = {
s.split ("[0-9]")
.filter (_.exists (ch => ch.isUpper))
.filter (_.exists (ch => ch.isLower))
.maxBy (_.length)
}
上面的函數(shù)解決了這個(gè)問題。如果輸入字符串不包含任何合適的子字符串,將會拋出
UnsupportedOperationException。
總結(jié)
Scala具有令人難以置信的強(qiáng)大的集合API,你可以利用它做很多的事情。 此外,相同的事情可以以不同的方式進(jìn)行,例如: 上面的歐拉函數(shù)例子。 Scala的API是很豐富的,我們需要很多時(shí)間和練習(xí)來學(xué)習(xí)它。