Scala集合笔记
Scala集合
- 可变集合
- 不可变集合(可以安全地并发访问)
- 默认都是不可变集合,Scala集合都提供了可变和不可变的版本
- Scala的集合有三大类:序列Seq(有序、线性)、集Set、映射Map(Key->Value,都实现了iterable特质
- Scala 数组和java 数组可以互操作
遍历集合元素
for(element<-array)
scala数组
-
长度固定则使用Array,不固定则使用 ArrayBuffer
-
提供初始值时不使用new
- Array(100):含有元素100的长度为1的数组
- new Array(100):长度为100的数组
-
[Any]泛型表示该数组可以存放任意类型。
-
在没有赋值的情况下,各个元素值为0。
-
用(i)来访问下标为i 的元素
-
用for(element<- array)来遍历内容
-
Scala 数组和 Java 数组互操作,Scala Array 对应的是java 中的数组,不在Scala 集合框架层 次之内
-
array++:list 把list加到array尾部
-
++= 可以追加任何一个集合
-
多维数组
-
// 定义2行3列的多维数组 val d = Array.ofDim[Int](2,3)
-
元组
- 元组是不同类型值的聚集 (1,3.14,“Fred”)
- 定义元组的方式
- 元组中最大只能有22个元素
- 获取元组方式,元组._1
- 试图通过一个类型参数来限定元组中所有 的元素的类型是错误的,需要单独限制,或者不限制
集合
- 集合三大类,序列、集合和映射
- 所有集合都扩展自Iterable 特质
- Iterable是指那些能生成用来访问集合中元素的 Iterator 的集合
Scala 提供了外部的或内部的迭代器。内部迭代器是由集合或者迭代器的拥有者自己遍历集合 ;外部迭代器是由外部调用者决定迭代的结束。
list(1 to 10:_*) \\扁平化 \\Cons(1,Cons(2,Cons(3,Cons(4,Cons(5,Cons(6,Cons(7,Cons(8,Cons(9,Cons(10,Nil))))))))))
list(1 to 10)\\Cons(Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),Nil)
集合运算
++: 合并
--: 去除公共元素
&: 交
| :并
&~:差
外部迭代器
- Iterable特质提供了iterator方法,iterator 方法返回了一个能用来遍历集合元素的外部迭代器
- Iterable 允许只使用集合部分元素的方法,比Traversable 更早提前停止迭代,从而性能上比Traversable稍稍提高。
Seq
- 序列有序,线性
- 可用于采样数据,计算滑动窗口
LinearSeq and IndexedSeq
LinearSeq特质代表能够分割为头元素+尾集合的集合,这个特质通过三个“假定高效”的抽 象方法来定义的: isEmpty , head , tail
indexedSeq与seq类似但随机访问效率高
Set
Set中每个元素都是唯一的
scala支持三种可变和不可变的set,分别是
-
treeSet
用红黑树实现,红黑树是一种试图保持平衡的数据结构。 它通过检查当前节点来查找树里的元素,如果当前节点大于期望值就查找左树,如果小于期望值就查找右树,正好等于期望值就找到了正确节点。要想创建一个TreeSet,必须提供隐式的Ordering 类型以便比较大于小于。
-
HashSet
用树结构实现集合。最大区别在于 HashSet 用元素的Hash值决定把元素放在哪个节 点上,HashSet 查找时的性能一般好于TreeSet
-
BitSet
用Long 型的序列来实现的,BitSet 通过把其底层Long值与欲保存的整数值位置设置为true来保存整数,BitSet经常用来在内存里跟踪和保存一大批标志位。
BitSet是位操作的对象,值只有0或1即false和true,内部维护了一个long数组,初始只有一个long,所 以BitSet最小的size是64,当随着存储的元素越来越多,BitSet内部会动态扩充,最终内部是由N个long来存 储,这些针对操作都是透明的。用1位来表示一个数据是否出现过,0为没有出现过,1表示出现过。使用用的 时候既可根据某一个是否为0表示,此数是否出现过。 一个1G的空间,有 8102410241024=8.5810^9bit,也就是可以表示85亿个不同的数
Map
不变的Map是有序的,可变的map 是无序的
可设定默认的value值
val mm = Map("zhangfei"->"zhangbashemao", "guanyu"->"qinglongyanyue")
.withDefaultValue("bishou")
当不是集合中的key时输出vlaue
集合运算
-
map
- flatmap:扁平化,将 元素抽出来
-
Filter
-
fold/reduce
-
fold 与reduce类似只是含有种子值
- foldLeft种子值在左
- foldright种子值在右
-
a|b|c|d|e
-
val listString = List("a","b","c","d") // :\代表foldRight,类似于reduceRight val x1 = (listString :\ "e")(_ +"|"+ _)
-
-
Flatten
- 通过flatmap和flatten操作将集合中每个元素通过定制函数映射为新的扁平化结果集合。
-
Scan
- 扫描,即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存
-
Zip
顾名思义,像拉链一样将两个集合链接起来。
Scala集合分类
- 不变集合
- Vector
- List
- Stream
- 可变集合
- ArrayBuffer
- ListBuffer
即时计算vs延时计算
顺序计算Vs并行计算
Vector
-
Vector 是由元素的下标组成的前缀树
-
Vector 类目前可以被认为是这么一个通用的不可变数据结构。Vector是一个带索引的不可变序列 容器,如果你更倾向于使用一个链式不可变集合容器,那么你可以选择List。
-
不能改变Vector,但是可以通过新生成Vector 的方式,为Vector 增加元素。
-
Vector作为一个集合类型,按地址随机存取元素效率是比较低的。但是Vector访问列表元素是高 效的,可以在常量时间完成。Vector在快速随机访问和快速随机存取方面是比较均衡的,Vectors现 在是immutable indexed sequences的默认实现。
-
不适合频繁的头尾分解
List
- 总在头上添加或者删除,性能表现不错
- List 由两个类组成,一个是代表空列表的Nil ,另一个是Cons
- List 继承自LinearSeq,适合头尾分解和头 部添加操作
- 不适合中间元素修改,那么前半个List 都要 重新生成,不如Vector 通用
Stream
由cons 格子和空 stream 组成,类似于List,延迟计算元素的值
构造stream
val s = 1 #:: {
println("HI")
2
} #:: {
println("BAI")
3
} #:: {
println("BAI")
3
} #:: Stream.empty
println(s)//Stream(1,2,3,3,?)
视图与并行集合
并行vs串行vs并发vs分布式
假设有AB两个任务,则串行、并行、并发的区别如图1所示。
串行
A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。
并行
并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。比如,A和B两个任务可以同时运行在不同的CPU线程上,效率较高,但受限于CPU线程数,如果任务数量超过了CPU线程数,那么每个线程上的任务仍然是顺序执行的。
并发
并发指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,而实际上是轮流穿插着执行,并发的实质是一个物理CPU在若干道程序之间多路复用,其目的是提高有限物理资源的运行效率。 并发与并行串行并不是互斥的概念,如果是在一个CPU线程上启用并发,那么自然就还是串行的,而如果在多个线程上启用并发,那么程序的执行就可以是既并发又并行的。
而分布式和并行的区别如下:
分布式
分布式在并行处理的基础上,强调任务正在执行的物理设备,如处理器、内存等等硬件,在物理上是分开的。而并行计算是指在一台计算机上的计算,在物理上不分开。
-
Scala集合默认是严格和串行计算的,严格计算是指非延迟计算
-
集合库通过两个标准机制来把默认执行方式转化为并行计算(par)或者延迟计算(view),分别是view和par,force方法是view方法的反操作,用来创建一个即时计算的新集合。Par方法的反向操作的seq。
aggregate
通常是par +aggreg结合使用。
val rdd1 = List(1 to 100: _*)//扁平化
val result: Int = rdd1.par.aggregate(0)(
//acc=0
(acc, number) => {//分片
val res1 = acc + number
println("par " + acc + " + " + number + " = " + res1)
res1
},
(par1, par2) => {//结合
val res2 = par1 + par2
println("com " + par1 + " + " + par2 + " = " + res2)
res2
}
)
println(result)
}