java字符串去掉一头一尾_快学Scala第13章----集合
本章要點(diǎn)
所有集合都擴(kuò)展自Iterable特質(zhì)
集合有三大類(lèi):序列、集、映射
對(duì)于幾乎所有集合類(lèi),Scala都同時(shí)提供了可變的和不可變的版本
Scala列表要么是空的,要么擁有一頭一尾,其中尾部本身又是一個(gè)列表
集是無(wú)先后次序的集合
用LinkedhashSet 來(lái)保留插入順序,或者用SortedSet來(lái)按順序進(jìn)行迭代
‘+’ 將元素添加到無(wú)先后次序的集合中; +: 和 :+ 向前或向后追加到序列; ++將兩個(gè)集合串接在一起; -和--移除元素
Iterable和Seq特質(zhì)有數(shù)十個(gè)用于常見(jiàn)操作的方法
映射、折疊和拉鏈操作是很有用的技巧,用來(lái)將函數(shù)或操作應(yīng)用到集合中的元素
主要的集合特質(zhì)
Scala集合繼承層級(jí)中的關(guān)鍵特質(zhì):
jihe.png
Iterable值的是那些能申城用來(lái)訪問(wèn)集合中所有元素的Iterator的集合,類(lèi)似于C++的迭代器。
val coll = ... //某種Iterable
val iter = coll.iterator
while (iter.hasNext) {
對(duì)iter.next() 執(zhí)行某種操作
}
Seq是一個(gè)有先后次序的值的序列,例如數(shù)字或列表。IndexedSeq允許我們通過(guò)使用下標(biāo)的方式快速訪問(wèn)元素。
Set是一組沒(méi)有先后次序的值的集合。在SortedSet中,元素是排序的。
Map是一組(key, value)對(duì)偶。 SortedMap按照鍵的排序的。
每個(gè)Scala集合特質(zhì)或類(lèi)都有一個(gè)帶有apply方法的伴生對(duì)象,這個(gè)apply方法可以用來(lái)構(gòu)建該集合的實(shí)例,而不用使用new,這樣的設(shè)計(jì)叫做"統(tǒng)一創(chuàng)建原則"。
可變和不可變集合
Scala同時(shí)支持可變和不可變的集合。Scala優(yōu)先采用不可變集合,因此你可以安全的共享其引用。任何對(duì)不可變集合的修改操作都返回的是一個(gè)新的不可變集合,它們共享大部分元素。
def digits(n: Int): Set[Int] = {
if (n < 0) digits(-n)
else if (n < 10) Set(n)
else digits(n / 10) + (n % 10)
}
這個(gè)例子利用遞歸不斷的產(chǎn)生新的集合,但是要注意遞歸的深度。
序列
最重要的不可變序列:
seq.png
Vector是ArrayBuffer的不可變版本,和C++的Vector一樣,可以通過(guò)下標(biāo)快速的隨機(jī)訪問(wèn)。而Scala的Vector是以樹(shù)形結(jié)構(gòu)的形式實(shí)現(xiàn)的,每個(gè)節(jié)點(diǎn)可以有不超過(guò)32個(gè)子節(jié)點(diǎn)。這樣對(duì)于有100萬(wàn)的元素的向量而言,只需要4層節(jié)點(diǎn)。
Range表示一個(gè)整數(shù)序列,例如0,1,2,3,4,5 或 10,20,30 . Rang對(duì)象并不存儲(chǔ)所有值而只是起始值、結(jié)束值和增值。
最有用可變序列:
seq2.png
列表
在Scala中,列表要么是Nil(空列表),要么是一個(gè)head元素加上一個(gè)tail,而tail又是一個(gè)列表。例如:
val digits = List(4,2)
digits.head // 4
digits.tail // List(2)
digits.tail.head // 2
digits.tail.tail // Nil
:: 操作符從給定的頭和尾創(chuàng)建列表:
9 :: List(4, 2) // List(9,4,2)
// 等同于
9 :: 4 :: 2 :: Nil // 這里是又結(jié)合的
遍歷鏈表:可以使用迭代器、遞歸或者模式匹配
def sum(lst: List[Int]): Int = {
if (lst == Nil) 0 else lst.head + sum(lst.tail)
}
def sum(lst: List[Int]): Int = lst match {
case Nil => 0
case h :: t => h + sum(t)
}
可變列表
可變的LinkedList既可以修改頭部(對(duì)elem引用賦值),也可以修改尾部(對(duì)next引用賦值):
val lst = scala.collection.mutable.LinkedList(1, -2, 7, -9)
// 修改值
var cur = lst
while (cur != Nil) {
if (cur.elem < 0) cur.elem = 0
cur = cur.next
}
// 去除每?jī)蓚€(gè)中的一個(gè)
var cur = lst
while (cur != Nil && cur.next != Nil) {
cur.next = cur.next.next
cur = cur.next
}
**注意: **如果你想要將列表中的某個(gè)節(jié)點(diǎn)變成列表的最后一個(gè)節(jié)點(diǎn),你不能夠?qū)ext引用設(shè)為Nil,而應(yīng)該將next引用設(shè)為L(zhǎng)inkedList.empty。也不要設(shè)為null,不然在遍歷該鏈表時(shí)會(huì)遇到空指針錯(cuò)誤。
集
集是不重復(fù)的元素的集合,與C++中的set相同。集并不保留元素插入的順序,默認(rèn)情況下,集以哈希集實(shí)現(xiàn)。
而鏈?zhǔn)焦<梢杂涀≡夭迦氲捻樞?#xff0c;它會(huì)維護(hù)一個(gè)鏈表來(lái)達(dá)到這個(gè)目的。
val weekdays = scala.collection.mutable.LinkedHashSet("Mo", "Tu", "We", "Th", "Fr")
對(duì)于SortedSet已排序的集使用紅黑樹(shù)實(shí)現(xiàn)的。Scala沒(méi)有可變的已排序的集,前面已經(jīng)講過(guò)。
集的一些常見(jiàn)操作:
val digits = Set(1,7,2,9)
digits contains 0 // false
Set(1, 2) subsetOf digits // true
val primes = Set(2,3,5,7)
digits union primes // Set(1,2,3,5,7,9)
// 等同于
digits | primes // 或 digits ++ primes
digits intersect primes // Set(2, 7)
// 等同于
digits & primes
digits diff primes // Set(1, 9)
// 等同于
digits -- primes
用于添加或去除元素的操作符
option.png
一般而言, + 用于將元素添加到無(wú)先后次序的集合,而+: 和 :+ 則是將元素添加到有先后次序的集合的開(kāi)頭或是結(jié)尾。
Vector(1,2,3) :+ 5 // Vector(1,2,3,5)
1 +: Vector(1,2,3) // Vector(1,1,2,3)
常用方法
Iterable特質(zhì)最重要的方法:
Iterable.png
Iterable2.png
Seq特質(zhì)在Iterable特質(zhì)的基礎(chǔ)上又增加的一些方法:
seq.png
seq2.png
將函數(shù)映射到集合
map方法可以將某個(gè)函數(shù)應(yīng)用到集合的每一個(gè)元素并產(chǎn)出其結(jié)果的集合。例如:
val names = List("Peter", "Paul", "Mary")
names.map(_.toUpperCase) // List("PETER", "PAUL", "MARY")
// 等同于
for (n
如果函數(shù)產(chǎn)出一個(gè)集合而不是單個(gè)值得話,則使用flatMap將所有的值串接在一起。例如:
def ulcase(s: String) = Vector(s.toUpperCase(), s.toLowerCase())
names.map(ulcase) // List(Vector("PETER", "peter"), Vector("PAUL", "paul"), Vector("MARY", "mary"))
names.flatmap(ulcase) // List("PETER", "peter", "PAUL", "paul", "MARY", "mary")
collect方法用于偏函數(shù)---并沒(méi)有對(duì)所有可能的輸入值進(jìn)行定義的函數(shù)。例如:
"-3+4".collect {case '+' => 1; case '-' => -1} // Vector(-1,1)
"-3+4".collect {case '-' => -1} // Vector(-1)
"-3+4".collect {case '+' => 1} // Vector(1)
"-3+4".collect {case '*' => 1} // Vector()
foreach方法將函數(shù)應(yīng)用到各個(gè)元素但不關(guān)心函數(shù)的返回值。
names.foreach(println)
化簡(jiǎn)、折疊和掃描
reduceLeft、reduceRight、foldLeft、foldRight、scanLeft、scanRight方法將會(huì)用二元函數(shù)來(lái)組合集合中的元素:
List(1,7,2,9).reduceLeft(_ - _) // ((1 - 7) - 2) - 9
List(1,7,2,9).reduceRight(_ - _) // 1 - (7 - (2 - 9))
List(1,7,2,9).foldLeft(0)(_ - _) // 0 - 1 -7 - 2 - 9
List(1,7,2,9).foldRight(0)(_ - _) // 1 - (7 - (2 - (9 - 0)))
(1 to 10).scanLeft(0)(_ + _) // Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)
拉鏈操作
前面的章節(jié)已經(jīng)講過(guò)拉鏈操作。除了zip方法外,還有zipAll和zipWithIndex方法
List(5.0, 20,0, 9.7, 3.1, 4.3).zipAll(List(10, 2), 0.0, 1) // List((5.0, 10), (20.0, 2), (9.7, 1), (3.1, 1), (4.3, 1))
"Scala".zipWithIndex // Vector(('S', 0), ('c', 1), ('a', 2), ('l', 3), ('a', 4) )
迭代器
迭代器的好處就是你不用將開(kāi)銷(xiāo)很大的集合全部讀進(jìn)內(nèi)存。例如讀取文件操作,Source.fromFile產(chǎn)出一個(gè)迭代器,使用hasNext和next方法來(lái)遍歷:
while (iter.hasNext)
對(duì) iter.next() 執(zhí)行某種操作
這里要注意迭代器多指向的位置。在調(diào)用了map、filter、count、sum、length等方法后,迭代器將位于集合的尾端,你不能再繼續(xù)使用它。而對(duì)于其他方法而言,比如find或take,迭代器位于已經(jīng)找到元素或已取得元素之后。
流
流是一個(gè)尾部被懶計(jì)算的不可變列表-----也就是說(shuō),只有當(dāng)你需要時(shí)它才會(huì)被計(jì)算。
def numsFrom(n: BigInt): Stream[BigInt] = n #:: numsFrom(n + 1)
val tenOrMore = numsFrom(10) // 得到一個(gè) Stream(10, ?) 流對(duì)象,尾部未被求值
temOrMore.tail.tail.tail // Stream(13, ?)
流的方法是懶執(zhí)行的。例如:
val squares = numsFrom(1).map(x => x * x) // 產(chǎn)出 Stream(1, ?)
// 需要調(diào)用squares.tail來(lái)強(qiáng)制對(duì)下一個(gè)元素求值
squares.tail // Stream(4, ?)
// 使用take和force強(qiáng)制求指定數(shù)量的值
squares.take(5).force // Stream(1,4,9,16,25)
**注意: ** 不要直接使用 squares.force, 這樣將會(huì)是一個(gè)無(wú)窮的流的所有成員求值, 引發(fā)OutOfMemoryError 。
懶視圖
應(yīng)用view方法也可以實(shí)現(xiàn)懶執(zhí)行,該方法產(chǎn)出一個(gè)其方法總是被懶執(zhí)行的集合。例如:
val powers = (0 until 1000).view.map(pow(10, _))
powers(100) //pow(10, 100)被計(jì)算,其他值的冪沒(méi)有被計(jì)算
和流不同,view連第一個(gè)元素都不求值,除非你主動(dòng)計(jì)算。view不緩存任何值,每次調(diào)用都要重新計(jì)算。
懶集合對(duì)于處理以多種方式進(jìn)行變換的大型集合很有好處,因?yàn)樗苊饬藰?gòu)建出大型中間集合的需要。例如:
(0 to 1000).map(pow(10, _)).map(1 / _) // 1
(0 to 1000).view.map(pow(10, _)).map(1 / _).force // 2
第一個(gè)式子 會(huì)產(chǎn)出兩個(gè)集合,第一個(gè)集合的每一個(gè)元素是pow(10, n),第二個(gè)集合是第一個(gè)集合中每個(gè)集合中的元素取倒數(shù)。 而第二個(gè)表達(dá)式使用了視圖view,當(dāng)動(dòng)作被強(qiáng)制執(zhí)行時(shí),對(duì)每個(gè)元素,這兩個(gè)操作是同時(shí)執(zhí)行的,不需要額外構(gòu)建中間集合。
與Java集合的互操作
java.png
scala.png
線程安全的集合
Scala類(lèi)庫(kù)提供了六個(gè)特質(zhì),你可以將它們混入集合,讓集合的操作變成同步:
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack
例如:
val scores = new scala.collection.mutable.HashMap[String, Int] with scala.collection.mutable.SynchronizedMap[String, Int]
當(dāng)然,還有更高效的集合,例如ConcurrentHashMap或ConcurrentSkipListMap,比簡(jiǎn)單的用同步方式執(zhí)行所有的方法更為有效。
并行集合
集合的par方法產(chǎn)出當(dāng)前集合的一個(gè)并行實(shí)現(xiàn),例如sum求和,多個(gè)線程可以并發(fā)的計(jì)算不同區(qū)塊的和,在最后這部分結(jié)果被匯總到一起。
coll.par.sum
你可以通過(guò)對(duì)要遍歷的集合應(yīng)用par并行for循環(huán)
for(i
par.png
而在 for/yield循環(huán)中,結(jié)果是依次組裝的:
for(i
yield.png
這里要注意變量是共享變量,還是循環(huán)內(nèi)的局部變量:
var count = 0
for (c
**注意: **par方法返回的并行集合的類(lèi)型為擴(kuò)展自ParSeq、ParSet或ParMap特質(zhì)的類(lèi)型,所有這些特質(zhì)都是ParIterable的子類(lèi)型。這些并不是Iterable的子類(lèi)型,因此你不能將并行集合傳遞給預(yù)期Iterable、Seq、Set、Map方法。你可以用ser方法將并行集合轉(zhuǎn)換回串行集合,也可以實(shí)現(xiàn)接受通用的GenIterable、GenSeq、GenSeq、GenMap類(lèi)型的參數(shù)的方法。
**說(shuō)明: **并不是所有的方法都可以被并行化。例如reduceLeft、reduceRight要求每個(gè)操作符按照順序先后被應(yīng)用。
總結(jié)
以上是生活随笔為你收集整理的java字符串去掉一头一尾_快学Scala第13章----集合的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 信息学奥赛一本通 1307:【例1.3】
- 下一篇: 丁丁打折网卷能用吗_超市货架上就能买到的