知识点
- 所有的集合都扩展自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
方法将产出所有中间结果和最后的结果
1. object IterableLearn extends App{ 2. println("--" * 10) 3. 4. // 相当于(((1 + 2) + 3) + 4) + 5 5. println("从1加到5的和") 6. println(List(1, 2, 3, 4, 5).reduceLeft(_ + _)) 7. 8. 9. println("初始值为6,从左到右将1加到5的和") 10. println(List(1, 2, 3, 4, 5).foldLeft(6)(_ + _)) 11. println((6 /: List(1, 2, 3, 4, 5))(_ + _)) 12. 13. println("统计字符串中每个字符出现的次数") 14. val m = (Map[Char, Int]() /: "Mississipyyz") ( 15. (m, c) => m + (c -> (m.getOrElse(c, 0) + 1)) 16. ) 17. for((k, v) <- m) { 18. println(k, v) 19. } 20. 21. println("产出所有中间结果和最后的和") 22. println((1 to 10).scanLeft(0)(_ + _)) 23. 24. } 25. 26. // 输出 27. -------------------- 28. 从1加到5的和 29. 15 30. 初始值为6,从左到右将1加到5的和 31. 21 32. 21 33. 统计字符串中每个字符出现的次数 34. (s,4) 35. (y,2) 36. (M,1) 37. (i,3) 38. (p,1) 39. (z,1) 40. 产出所有中间结果和最后的和 41. 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))