Scala学习笔记 A2/L1篇 - 集合 Collections

教材:快学Scala

chapter 13. 集合 Collections

  • 集合=Collection 集=Set
  • 所有集合都extends Iterable特质
  • 集合分为三大类 Seq Set Map,几乎所有集合类都有mutableimmutable两个版本
  • List 要么为Nil(空表) 要么有headtail 其中tail本身又是一个List
  • 关于元素添加:+用于添加到无先后次序的集合中(Set Map),+: :+用于添加到Seq ++串接集合 - --移除元素

13.1 主要的集合traits

  • Iterable:能生成用来访问集合中所有元素的迭代器
val coll = ... // some Iterable
val iter = coll.iterator
while (iter.hasNext) 
    iter.next() // do something
  • Seq 有先后次序的值的序列(数组/列表) IndexedSeq 允许下标快速访问元素 如 ArrayBuffer
  • Set 一组没有先后次序的值 SortedSet 以排序后的顺序访问元素
  • Map 一组kv对偶 SortedMap 按照键排序访问元素
  • 与Java相比的改进:
    Scala:Map|Seq|SetIterable
    Java:Set|List|QueueCollection, Hashtable|Hashmap|SortedMapMap
    Scala:ArrayBufferIndexedSeq, List|LinkedListLinearSeq 数组和列表没有直接继承同一个trait
    Java:ArrayList|LinkedListList
  • 统一创建原则(uniform creation principle) 所有Scala集合都有一个带apply方法的伴生对象,用来构建该集合的实例
    Iterable(0xFF, 0xFF00, 0xFF0000)
    Set(Color.RED, Color.GREEN, Color.BLUE)
    Map(Color.RED -> 0xFF, Color.GREEN -> 0xFF00, Color.BLUE -> 0xFF0000)
    SortedSet("hello", "world")

13.2 mutable immutable

  • immutable集合从不改变,可以安全地共享其引用 每一步添加|删除|修改操作都生成一个新的集合
  • 默认的是immutable Map Predef.Map scala.collection.Map 都是指 scala.collection.immutable.Map

13.3 序列 Seq

  • immutable
    Vector|Range<t>IndexedSeq<t>Seq
    List|Stream|Stack|Queue<t>LinearSeq<t>Seq
    Vector ArrayBuffer的不可变版本 下标访问 树形结构实现(32叉树)
    Range 整数序列 只存储起始值、结束值和增值 用 to until 构造
  • mutable
    Array是Java数组实现,Array[T]在JVM就是一个T[]
    ArrayBuffer<t>IndexedSeq<t>Seq
    (QueueMutableList)|LinkedList|DoubleLinkedList<t>LinerSeq<t>Seq
    Stack<t>Seq
    PriorityQueue<t>Iterable

13.4 immutable.List

List(不可变) LinkedList|DoubleLinkedList(可变)
递归定义列表:List要么是Nil,要么是一个head元素加一个tail,而tail又是一个List

val digits = List(4, 2) // head = 4, tail = List(2)
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 sum2(lst: List[Int]): Int = lst match { // 递归 模式匹配
    case Nil => 0
    case h :: t => h + sum2(t) // 将列表析构成head和tail
}
List(9, 4, 2).sum // 最方便

13.5 mutable.LinkedList|DoubleLinkedList

对应传统单向链表(LinkedList)/双向链表(DoubleLinkedList)的定义,有nextprev指针,节点元素elem
修改某个节点为最后一个节点:cur = LinkedList.empty 不能赋值为Nil 更不能赋值为null会产生空指针错误

// 将链表上所有负值改成0
val lst = scala.collection.mutable.LinkedList(1, -2, 7, -9)
val cur = lst
while (cur != Nil) {
    if (cur.elem < 0) cur.elem = 0
    cur = cur.next
}

// 链表上每两个元素去掉一个
var cur = lst
while (cur != Nil && cur.next != Nil) {
    cur.next = cur.next.next
    cur = cur.next
}

13.6 集 Set

  • 不重复元素的集合,默认是哈希集实现,在哈希集上查找比在数组和列表快得多
  • 两种有序的集:可变的链式哈希集、排序集
    mutable.LinkedHashSet 记住元素被插入的顺序,通过维护一个链表
    SortedSet 排序后的顺序,用红黑树实现
  • BitSet 位集
  • contains检查包含元素 subsetOf检查包含子集
val digits = Set(1, 7, 2, 9)
digits contains 0 // false
Set(1, 2) subsetOf digits // true
  • 并集/交集/差集
    并集操作 union|++
    交集操作 intersect&
    差集操作 diff&~--

13.7 添加/删除元素的操作符

:的操作符 :偏向集合操作数一侧,元素::lst添加元素 lst2:::lst合并列表
Seq:+元素向后追加元素,元素+:Seq向前追加元素
无序集合+元素 无序集合+(e1,e2,...) 适用于Map Set
集合-元素 集合-(e1,e2,...) 集合--集合 移除元素
集合++集合 合并集合
对于列表,优先使用:: :::

13.8 常用方法

  • trait Iterable
    head 第一个元素 last 最后一个元素 headOption lastOption 以Option返回
    tail init 除去第一个/最后一个元素剩下部分
    count(fun) 计数符合条件 forall(fun) 是否全部满足 exist(fun) 是否存在
    length isEmpty
    filter filterNot partition(fun) 将filter和filterNot的结果组成pair
    takeWhile dropWhile span(fun) 组成pair
    take drop splitAt(n) 组成pair takeRight dropRight slice(from, to)
    mkString
  • trait Seq
    contains(elem) containsSlice(Seq) startsWith(Seq) endsWith(Seq)
    indexOf(elem) lastIndexOf(elem) indexOfSlice(Seq) indexWhere(fun)
    padTo(n, fill) 补齐元素fill至长度n
    reverse 序列反向
    sorted 使用元素本身大小排序 x < y
    sortWith(less) 使用二元函数less排序 less(x, y) = x < y
    sortBy(f) 根据f(x)排序 f(x) < f(y)
    permutations 遍历全排列的迭代器
    combination(n) 遍历长度为n的组合的迭代器
  • 统一返回类型(uniform return type)原则:这些方法不改变原集合,而是返回一个与原集合类型相同的新集合

13.9 Mapping a Function

map flatMap collect foreach
for (n <- names) yield n.toUpperCase 等价于 names.map(_.toUpperCase)
collect用于偏函数(partial function) 即没有对所有可能输入进行定义的函数
"-3++4".collect { case '+' => 1; case '-' => -1 } // Vector(-1, 1, 1)

13.10 化简reduce 折叠fold 扫描scan

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 第一个为柯里化参数,通过初始值类型推断操作符的类型定义 
(0 /: List(1, 7, 2, 9))(_ - _) // 等价于foldLeft, :\ 是foldRight

// foldLeft取代循环 统计字母频率
(Map[Char, Int]() /: "Mississippi") { (m, c) => m + (c -> (m.getOrElse(c, 0) + 1)) }

// scan:就是存储fold中间的每一步结果
(1 to 10).scanLeft(0)(_ + _) // Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)

13.11 拉链zip

((price zip quantities) map { p => p._1 * p._2 }) sum

// zipAll 指定较短列表的缺省值
List(5.0, 20.0, 9.95).zipAll(List(10, 2), 0.0, 1) // List(5.0, 20.0, 9.95).zipAll(List(10, 2), 0.0, 1)

"Scala".zipWithIndex // Vector((S,0), (c,1), (a,2), (l,3), (a,4))

13.12 迭代器iterator

  • iterator方法获得集合的迭代器 用于完整构造需要巨大开销的集合,例如Source.fromFile
  • grouped(n)(分成n组) sliding(n)(大小为n的滑窗) 返回一个迭代器
val it1 = (1 to 100).grouped(10)
val it2 = (1 to 100).sliding(10)
  • 利用迭代器遍历 while (iter.hasNext) { iter.next } for (elem <- iter) { elem }
  • Iterator类定义了一些与集合方法使用起来完全相等的方法除head,last,tail,init,takeRight等意外都支持,使用部分方法后迭代器将指向末尾不能再用
  • 可以先用toArray toSeq等等把元素拷到新集合再处理

13.13 流Stream

流是迭代器的immutable替代品,是一个尾部被懒计算的不可变列表
#:: 构造流操作符

def numsFrom(n: BigInt): Stream[BigInt] = n #:: numsFrom(n + 1)
val tenOrMore = numsFrom(10) // Stream(10, ?) 懒求值
tenOrMore.tail.tail.tail // Stream(13, ?)
val squares = numsFrom(1).map(x => x * x) // Stream(1, ?) 流的方法是懒执行的

// 强制求所有值,先用take,再用force 【注意 不要直接force】
squares.take(50).force

// 迭代器转换为流 toStream
val words = Source.fromFile("xxx.txt").getLines.toStream

13.14 懒视图 Lazy Views

集合的view方法:懒操作集合的计算,且不缓存计算结果,每次调用重新计算

val powers = (0 until 1000).view.map(pow(10, _)) // 此时一个值都没有计算
powers(100) // 计算pow(10,100) 但不缓存值

(0 to 1000).map(pow(10, _)).map(1 / _)      // 有中间计算结果的集合
(0 to 1000).view.map(pow(10, _)).map(1 / _) // 对每个元素 两个map同时执行不需要构建中间集合

13.15 与Java集合的互操作

Java的集合和Scala的集合之间互相转换的方法 JavaConversions

13.16 线性安全的集合

用于多线程访问一个可变集合时确保线性安全
方法1. 使用Scala的同步集合 Synchronized*
* 可以是 Buffer Map PriorityQueue Queue Set Stack
方法2. 使用java.util.concurrent包中的类,再转换为Scala集合

13.17 并行集合

par方法返回集合的一个并行实现 使之尽可能的并行执行集合方法
coll.par.sum
如果并行运算修改了共享的变量(如计数器的实现),则共享变量的结果无法预知

练习答案

def indexes(str: String) =
str.zipWithIndex.foldLeft(Map[Char, scala.collection.SortedSet[Int]]()){
    (m, p) => m + (p._1 -> (m.getOrElse(p._1, scala.collection.SortedSet[Int]()) + p._2))
}
def indexes(str: String) =
str.zipWithIndex.foldLeft(Map[Char, List[Int]]()){
    (m, p) => m + (p._1 -> (m.getOrElse(p._1, List[Int]()) :+ p._2))
}
def dropZeros(lst: List[Int]): List[Int] = lst match {
    case Nil => Nil
    case h :: t => if (h == 0) dropZeros(t) else h :: dropZeros(t)
}
  1. def names2nums(s: Seq[String], m: Map[String, Int]) = s.flatMap(m.get(_))
// 看了github别人的答案,这里res必须为Any类型才可以,"since its the closest common parent type for String and T"
def myMkString[T](coll: Iterable[T], before: String, between: String, after: String) = 
if (coll.isEmpty) before + after else before + coll.reduceLeft((res: Any, elem: T) => res + between + elem) + after

// reduceLeft sucks
def myMkString[T](coll: Iterable[T], before: String, between: String, after: String) = 
if (coll.isEmpty) before + after else coll.foldLeft(before)((res, elem) => res + elem + between).init + after

myMkString[Int](List(1, 2, 3, 4, 5), "<", "-", ">")
myMkString[Int](List(), "<", "-", ">")
def reverse1(lst: List[Int]) = (lst :\ List[Int]())((e, l) => l :+ e)
def reverse2(lst: List[Int]) = (List[Int]() /: lst)((l, e) => e +: l)
  1. def twoDimensionize(a: Seq[Int], n: Int) = a.grouped(n).toArray
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Scala的集合类可以从三个维度进行切分: 可变与不可变集合(Immutable and mutable coll...
    时待吾阅读 5,798评论 0 4
  • 可变和不可变集合 Scala中的集合可分为可变集合和不可变集合。可变集合可以当场被更新,不可变集合本身是不可变的。...
    liqing151阅读 200评论 0 0
  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 3,417评论 1 24
  • 数组是一种可变的、可索引的数据集合。在Scala中用Array[T]的形式来表示Java中的数组形式 T[]。 v...
    时待吾阅读 938评论 0 0
  • 你已经20多岁了,虽然在父母眼里你仍是个孩子,可是无论怎样你须得长大了,像个大人一样陪着父母唠唠家长里短。你或许现...
    凉城未凉1983阅读 1,006评论 0 4