Scala 自学笔记 集合

Scala集合的主要特质

Iterator,用来访问集合中所有元素

val coll = ... // 某种Iterable
val iter = col.iterator
while(iter.hasNext)
  iter.next()


Seq是一个有先后次序的值得序列,比如数组或列表。

IndexSeq允许我们通过整形的下表快速访问任意元素,如ArrayBuffer是带下标的。

Set是一组没有先后次序的值,在SortedSet中,元素以某种排过序的顺序被访问。

Map是一组(K,V)对偶,SortedMap按照键的排序访问。

每个Scala集合特质或类,都有一个带有apply方法的伴生对象,这个apply方法可以用来构建该集合中的实例。
Iterable(0xFF, 0xFF00, 0xFF0000)
set(color.RED, color.GREEN, Color.BLUE)
Map(color.RED -> -0xFF0000, Color.GREEN -> 0xFF00, Color.BLUE -> 0xFF)
SortedSet("Hello" , "World")

可变和不可变集合

<pre name="code" class="java">scala.collection.mutable.Map    //可变
scala.collection.immutable.Map //不可变 
scala.collection.Map //超类
Scala优先采用不可变集合, scala.collection 包中的伴生对象产出不可变的集合
scala.collection.Map("Hello" -> 42) //不可变映射
因为Scala包和Preder对象总是被引入,他们都指向不可变特质的类型别名List、Set和Map.
Preedef.Map和scala.collection.immutable.Map是一回事

import scala.collection.mutable
用Map得到不可变,用mutable.Map得到可变的。

 

序列

Vector是ArrayBuffer的不可变版本,一个带下标的序列,支持快捷的随机访问,以树形结构的形式实现。
Range表示已个整数序列,只存储 起始值,结束值和增值, 用 to 和 until 方法来构造Range对象。


列表

列表要么是Nil(空表),要么是一个head元素和一个tail,tail又是一个列表。
val digits = List(4,2)
digits.head <span style="white-space:pre">		</span>//4
digits.tail <span style="white-space:pre">		</span>// List(2)
digits.tail.head <span style="white-space:pre">	</span>// 2
digits.tail.tail <span style="white-space:pre">	</span>//Nil

:: 操作符从给定的头和尾创建一个新的列表。
9 :: List(4,2) // List(9,4,2)
9 :: 4 :: 2 :: Nil  // :: 是右结合,列表从末端开始构建
9 :: ( 4 :: (2 :: Nil ) )

迭代, 除了遍历外,可以用 递归 模式匹配

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) // h 是 lst.head, 而t是lst.tail, ::将列表“析构”成头部和尾部
}
直接使用List的方法
List(9,4,2).sum

可变列表

LinkedList, elem指向当前值,next指向下一个元素
DoubleLinkedList多带一个prev
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
} // (1,0,7,0) ,将所有负值改为0
var cur = lst 
while(cur != Nil && cur.next != Nil){
  cur.next = cur.next.next
  cur = cur.next
}// 去除每两个元素中的一个
注:当要把某个节点变为列表中的最后一个节点,不能讲next 设为Nil 或 null, 而将它设为LinkedList.empty。

不重复元素的集合,以哈希集实现,元素根据hashCode方法的值进行组织
Set(2,0,1) + 1 // (2,0,1)

LinkedHashSet,链式哈希集 记住元素被插入的顺序
val weekdays = scala.collection.mutable.LinkedHashSet(1,2,3,4)

排序的集
scala.collection.immutable.SortedSet(1,2,3,4) // 用红黑树实现的
Scala 2.9没有可变的已排序集,用java.util.TreeSet

位集(bit set), 以一个字位序列的方式存放非负整数,如果集中有i,则第i个字位是1
高效的实现,只要最大元素不是特别大。
Scala提供 可变和不可变的两个 BitSet类

contains 检查是否包含, subsetOf 检查集的所有元素是否被另一个集包含
val digits = Set(1,7,2,9)
digits contains 0 // false
Set(1,2) subsetOf digits // true
union intersect diff 方法,也可写作| ,&, &~
union 还可以写成 ++, diff 写作 --
val primes = Set(2, 3, 5, 7)
digits union primes // Set(1,2,3,5,7,9)
digits & primes // Set (2,7)
digits -- primes // Set(1,9)

一般而言,+用于将元素添加到无先后次序的集合,而+:和:+则是将元素添加到有先后次序的集合的开头或末尾。
Vector(1,2,3) :+ 5 //Vector(1,2,3,5)
1 +: Vector(1,2,3) //Vector(1,1,2,3) 
以冒号结尾的操作符,+:是右结合的,这些操作符都返回新的集合

可变集合有 +=操作符 用于修改左侧操作元
val numbers = ArrayBuffer(1,2,3)
numbers += 5 // 将 5 添加到 numbers

不可变集合,可以在var上使用+=或:+=
var numbers = Set(1,2,3)
numbers += 5 // numbers 设为不可变的集numbers + 5
var numberVector = Vector(1,2,3)
numbersVector :+= 5 // 向量没有+操作符,只有:+

移除元素
Set(1,2,3) -2 // Set(1,3)

++来一次添加多个元素, -- 一次移除多个元素
col1 ++ col2 


Iterable特质APi

Seq特质API


将函数映射到集合

map方法
val names = List("Peter" , "Paul", "Mary")
names.map(_.toUpperCase) // 等同于 for(n <- names) yield n.toUpperCase

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 方法用于 partial function,那些并没有对所有可能的输入值进行定义的函数, 产出被定义的所有参数的函数值得集合
"-3+4".collect(case '+' -> 1; case '-' -> -1) // vector(-1,1)

foreach方法
names.foreach(println)

化简、折叠和扫描

List(1,7,2,9).reduceLeft(_ - _) 
( ( 1 - 7 ) - 2 ) - 9 = 1- 7 - 2 - 9 = -17

List(1,7,2,9).reduceRight(_ - _) 
1 - ( 7 - ( 2 - 9  ) ) = 1-7 + 2 -9 = -13

以不同首元素开始计算
List(1,7,2,9).foldLeft(0)(_ - _)
0-1-7-2-9 = -19
List(1,7,2,9).foldLeft(" ")(_ + _) // 由柯里化判断第二个参数类型定义(String, Int) => String
" " +1 + 7+2+9 = " 1729"
(0 /: List(1,7,2,9))(_ - _) //  /:操作符代替了foldLeft操作
Scala 也提供了foldRight 和 :\的变体
折叠有时可以代替循环,比如计算字母出现频率
val freq = scala.collection.mutable.Map[Char, Int]() // 可变映射
for( c <- "Mississippi") 
  freq(c) =freq.getOrElse(c,0)+1 // Map('i' ->4, 'M' -> 1, 's' -> 4, 'p' ->2)

折叠实现
(Map[Char, Int]() /:"Mississippi"){
  (m,c) => m + (c -> (m.getOrElse(c,0) +1)
}// 这里的 Map是不可变,每次计算出一个新的Map

scanLeft,scanRight, 得到包含所有中间结果的集合
(1 to 10).scanLeft(0)(_ + _)
Vector(0,1,3,6,10,15,21,28,36,45,55)

拉链操作

zip
val prices = List(5.0,20.0,9.95) // 价格
val quantities = List(10,2,1)     //数量
prices zip quantities 得到一个List[(Double, Int)] , 一个个对偶的列表
List[(Double, Int)] = List( (5.0, 10), (20.0, 2 ), (9.95, 1))
计算总价
( (prices zip quantities) map {p => p._1 * p._2}) sum

如果两个集合数量不一致
List( 5.0, 20.0, 9.95 ) zip List(10, 2) // List((5.0, 10), (20.0, 2))

zipAll 指定短列表的缺省值:第二个参数补充左边,第三个参数补充右边
List(1,1).zipAll(List(2),6,7) // List((1,2),(1,7))
List(1).zipAll(List(2,3),6,7)// List((1,2), (3,6))

zipWithIndex, 返回对偶列表,第二个组成部分是元素下标
"Scala".zipWithIndex // Vector(('S',0),('c',1),('a',2),('l',3),('a',4))
求最大编码的值得下标为
"Scala".zipWithIndex.max._2

迭代器 (相对于集合而言是一个“懒”的替代品,只有在需要时才去取元素,如果不需要更多元素,不会付出计算剩余元素的代价)

对于那些完整构造需要很大开销的集合,适合用迭代器
如Source.fromFile产出一个迭代器,因为整个文件加载进内存不高效。
迭代器的两种用法
while(iter.hasNext)
  iter.next()
for(elem <- iter)
  对elem操作
上述两种循环都会讲迭代器移动到集合末端,不能再被使用,
调用 map filter count sum length方法后, 跌倒器也会位于集合的末端,不能使用
find 或 take  , 迭代器位于找到的元素之后

流(stream)

迭代器每次调用next都会改变指向,
如果要缓存之前的值,可以使用流
流是一个尾部被懒计算的不可变列表,也就是说只有需要时才计算
def numsForm(n:BigInt) : Stream[BigInt] = n #:: numsForm(n+1) // #:: 操作符 构建出来的是一个流
var tenOrMore = numsForm(10) // Stream(10,?), 其尾部是未被求值得
tenOrMore.tail.tail.tail // Stream(13,?)
val squares = numsForm(1).map{ x=> x*x) // Stream(1,?)

take 可以一次获得多个值, force强制求值
squares.take(5).force // Stream(1,4,9,16,25)
squares.force // 会尝试对一个无穷流的所有成员求值,最后OutOfMemoryError

迭代器可以用来构造一个流
Source.getLines返回一个Iterator[String],用这个迭代器,对于每一行只能访问一次,而流将缓存访问过的行,允许重新访问
val words = Sourcce.fromFile("/usr/share/dict/words").getLines.toStream
words // Stream(A, ?)
words(5) // Aachen
words // Stream(A, A'o, AOL, AOL's, Aachen, ?)

懒试图(应用于集合)

类似流的懒理念
与流的不同
1、连第一个元素都不会求值
2、不会缓存求过的值

val powers = (0 unti 1000).view.map(pow(10,_))
powers(100) // pow(10,100)被计算,其他值未计算,同时也不缓存,下次pow(10,100)将重新计算

force方法可以对懒视图强制求值,得到与原集合相同类型的新集合,
懒试图的好处:可以避免在多种变换下产生的中间集合
(0 to 1000).map(pow(10,_)).map(1/_) //先第一个map,再第二个map, 构建了一个中间集合
(0 to 1000).view.map(pow(10,_)).map(1/_).force // 记住两个map操作,每个元素被两个操作同时执行,不需要额外构中间集合

与Java集合的互操作

import scala.collection.JavaConversions._
val props:scala.collection.mutable.Map[String,String] = System.getProperties()
如果担心那些不需要的隐式转换也被引入的话,只引入需要的即可
import scala.collection.JavaConversions.propertiesAsScalaMap

这些转换产出的是包装器,让你可以使用目标接口来访问原本的值
props("name") = "clat" //props既是包装器
包装器将调用底层Properties对象的put("name","clat")



线程安全的集合

Scala类库提供了六个特质,将他们混入集合,让集合的操作变成同步
SynchromizedBuffer
SynchromizedMap
SynchromizedPriorityQueue
SynchromizedQueue
SynchromizedSet
SynchromizedStack

val scores =new scala.collection.collection.mutable.HashMap[String, Int] with scala.collection.mutalbe.SynchronizzedMap[String,Int]
注:这里可以确保scores不会被破坏,任何操作都必须先完成,其他线程才可执行另一个操作。但并发修改和遍历集合并不安全。

通常来说,最好使用java.util.concurrent包中的类.

并行集合

为了更好利用计算机的多个处理器,支持并发通常是必需的
如果coll是个大型集合,那么
coll.par.sum  //并发求和,par方法产出当前集合的一个并行实现,该实现会尽可能地并行执行集合方法
coll.par.count(_ % 2 ==0) //计算偶数的数量
对数组、缓冲、哈希表、平衡树而言,并行实现会直接重用底层实际集合的实现,所以很高效。

可以通过对要遍历的集合应用.par并行化for循环
for( i <- (0 until 100).par) print( i + " " ) //数字是按照作用于该任务的线程产出的顺序输出

在for/yield循环中,结果是依次组装的
for( i <- (0 until 100).par) yield i +" "

par返回的并行集合扩展自ParSeq ParSet Parmap,都是ParIterable的子类型,不是Iterable的子类型,所以不能将并行集合传递给预期Iterable Seq Set Map的方法。
可以用ser方法将并行集合转换回串行的版本。
只有可以自由结合的操作 可以用平行集合
(a op b) op c = a op( b op c), 加是可自由结合的
(a -b ) -c != a - (b -c)  减法不是自由结合

有一个fold方法对集合的不同部分进行操作,但是不像foldLeft和foldRight那样灵活,
该操作符的两个操作元都必须是集合的元素类型,要求fold的参数类型与集合元素一样,不像上面foldLeft,参数是String, 集合是Int 那样
coll.par.fold(0)(_ + _)

aggregate方法,可以解决上面的问题,该操作符应用于集合的不同部分,然后再用你另一个操作符组合结果
str.par.aggregate(Set[Char]())(_ + _, _ ++ _) //等同于 str.foldLeft(Set[Char]())(_ + _)
产出一个str中所有不同字符的集







  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值