知识点
- 所有的集合都扩展自Iterable特质
- 集合有三大类,分别为序列、集和映射
- 几乎所有集合类,Scala都同时提供了可变和不可变的版本
- Scala列表要么是空的,要么拥有一头一尾,其中尾部本身又是一个表列
- 集是无先后次序的集合
- 用LinkedHashSet来保留插入顺序,或用SortedSet来按顺序进行迭代
- +将元素添加到无先后次序的集合中;+:和:+向前或向后追加到序列;++将两个集合串接在一起;-和--移除元素
- 映射、折叠和拉链操作是很有用的技巧,用来将函数和操作应用到集合中的元素
主要的集合特质
- Iterable指的是那些能生成用来访问集合中所有元素的Iterator的集合
- Seq是一个有先后次序的值的序列,如数组或列表。IndexedSeq允许我们通过整型的下标快速地访问任意元素
- Set是一组没有先后次序的值。SortedSet中,元素以某种排过序的顺序被访问
- Map是一组键值对,SortedMap按照键的排序访问其中的实体
- 每个Scala集合特质或类都有一个带有apply方法的伴生对象,这个apply方法可以用来构建该集合中的实例
可变和不可变集合
- Scala同时支持可变的和不可变的集合,不可变集合可以安全地共享其引用,甚至在一个多线程的应用程序当中也没有问题
- Scala优先采用不可变集合
- 可以基于不可变的老集合创建新集合,老集合保持不变
列表
- 在Scala中,列表要么是Nil,要么是一个head元素加上一个tail,而tail又是一个列表
- ::操作符从给定的头或尾创建一个新的列表,
9 :: List(4, 2)
,::是右结合的,通过此操作符,列表将从末端开始构建
/**
* @author Yezhiwei
* @date 18/1/10
*/
object IterableLearn extends App{
val digits = List(4, 2)
println("head " + digits.head)
println("tail " + digits.tail)
// 右结合性
println(9 :: digits)
println(List(1, 3) :: 9 :: Nil)
// 求列表的和
println(digits.sum)
}
// 输出
head 4
tail List(2)
List(9, 4, 2)
List(List(1, 3), 9)
6
Set
- Set是不重复元素的集合,已经存在的元素无法再次加入
- Set并不保留元素插入的顺序,缺省情况下以哈希集实现的,其元素根据hashCode方法的值进行组织
scala.collection.mutable.LinkedHashSet
链式哈希集可以记住元素被插入的顺序scala.collection.mutable.SortedSet
按照已排顺序来访问集中的元素(用红黑树实现的)
object IterableLearn extends App{
// 1 已经存在,不会被重复添加
println(Set(1, 2, 3) + 1)
// 链式哈希集可以记住元素被插入的顺序
val weekdays = scala.collection.mutable.LinkedHashSet("Mo", "Tu", "We", "Th", "Fr")
println("weekdays " + weekdays)
// 按照已排顺序来访问集中的元素
println(scala.collection.mutable.SortedSet(2, 4, 1, 5))
val digitsSet = Set(2, 5, 9)
// 检查某个值是否在集合中
println(digitsSet.contains(3))
println(digitsSet.contains(5))
// 某个集合中的所有元素是否包含在另一个集合中
println(Set(2, 4).subsetOf(digitsSet) )
println(Set(2, 5).subsetOf(digitsSet) )
val set1 = Set(1, 2)
val set2 = Set(2, 4)
// 并 | 或 ++ 或 union
println("====并 | 或 ++ 或 union====")
println(set1 | set2)
println(set1 ++ set2)
println(set1 union set2)
// 差 &~ 或 -- 或 diff
println("====差 &~ 或 -- 或 diff====")
println(set1 &~ set2)
println(set1 -- set2)
println(set1 diff set2)
// 交 & 或 intersect
println("====交 & 或 intersect====")
println(set1 & set2)
println(set1 intersect set2)
}
// 输出结果
Set(1, 2, 3)
weekdays Set(Mo, Tu, We, Th, Fr)
TreeSet(1, 2, 4, 5)
false
true
false
true
====并 | 或 ++ 或 union====
Set(1, 2, 4)
Set(1, 2, 4)
Set(1, 2, 4)
====差 &~ 或 -- 或 diff====
Set(1)
Set(1)
Set(1)
====交 & 或 intersect====
Set(2)
Set(2)
将函数映射到集合
- map方法可以将某个函数应用到集合中的每个元素并产出其结果的集合
val names = List("Perter", "Paul", "Mary")
println("names to upperCase" + names.map(_.toUpperCase))
// 输出
names to upperCaseList(PERTER, PAUL, MARY)
- 如果函数产出一个集合而不是单个值,将所有的值串接在一起,则用flatMap
def ulcase(s: String) = Vector(s.toUpperCase, s.toLowerCase)
// 函数返回的是一个值
println(names.map(ulcase(_)))
// 函数返回的是一个集合
println(names.flatMap(ulcase(_)))
// 输出
List(Vector(PERTER, perter), Vector(PAUL, paul), Vector(MARY, mary))
List(PERTER, perter, PAUL, paul, MARY, mary)
化简、折叠和扫描
s.reduceLeft(op)
将op
相继应用到所有元素;s.reduceRight(op)
方法同样,只不过它是从集合的尾部开始coll.foldLeft(init)(op)
init 初始值, 将op
应用到初始值及所有的元素- 折叠有时可以作为循环的替代,如下面计算字母出现的次数
scanLeft
和scanRight
方法将产出所有中间结果和最后的结果
object IterableLearn extends App{
println("--" * 10)
// 相当于(((1 + 2) + 3) + 4) + 5
println("从1加到5的和")
println(List(1, 2, 3, 4, 5).reduceLeft(_ + _))
println("初始值为6,从左到右将1加到5的和")
println(List(1, 2, 3, 4, 5).foldLeft(6)(_ + _))
println((6 /: List(1, 2, 3, 4, 5))(_ + _))
println("统计字符串中每个字符出现的次数")
val m = (Map[Char, Int]() /: "Mississipyyz") (
(m, c) => m + (c -> (m.getOrElse(c, 0) + 1))
)
for((k, v) <- m) {
println(k, v)
}
println("产出所有中间结果和最后的和")
println((1 to 10).scanLeft(0)(_ + _))
}
// 输出
--------------------
从1加到5的和
15
初始值为6,从左到右将1加到5的和
21
21
统计字符串中每个字符出现的次数
(s,4)
(y,2)
(M,1)
(i,3)
(p,1)
(z,1)
产出所有中间结果和最后的和
Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)
拉链操作
zip
这个方法之所以叫”拉链操作“,是因为它像拉链的齿状结构一样将两个集合结合在一起- 如将两个集合,把相互对应的元素结合在一起,如产品价格列表及相应的数量,
zip
方法将它们组成一个个对偶的列表
val prices = List(50.0, 20.0, 9.8)
val quantities = List(10, 20, 50)
println(prices zip quantities)
// 输出
List((50.0,10), (20.0,20), (9.8,50))
- 如果一个集合比另一个短,结果中的对偶数量和较短的那个集合的元素数量相同
zipAll
方法可指定短列表的缺省值,自身列表比较短的话使用thisElem进行填充,对方列表较短的话使用thatElem进行填充
// 将对应位置的元素组成一个pair,若列表长度不一致,自身列表比较短的话使用thisElem进行填充,对方列表较短的话使用thatElem进行填充
println(prices.zipAll(List(2, 3), 0, 3))
println(prices.zipAll(List(2, 3, 4, 6), 0, 3))
// 输出
List((50.0,2), (20.0,3), (9.8,3))
List((50.0,2), (20.0,3), (9.8,4), (0,6))
这一篇暂时总结这么多,在后面Spark算子中再详细了解,敬请期待下一篇~