第六章 集合
6.1集合简介
1)Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。
2)对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
3)Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象
4)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象
6.1.1 不可变集合继承图
1)Set、Map是Java中也有的集合
2)Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了
3)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range
4)String也是属于IndexedSeq
5)我们发现经典的数据结构比如Queue和Stack被归属到LinearSeq(线性序列)
6)大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
7)IndexedSeq和LinearSeq的区别:
(1)IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位
(2)LinearSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
6.1.2 可变集合继承图
6.2 数组
6.2.1 不可变数组
1)第一种方式定义数组
val arr1 = new Array[Int](10)
(1)new是关键字
(2)[Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
(3)(10),表示数组的大小,确定后就不可以变化
2)第二种方式定义数组
val arr1 = Array(1, 2)
(1)在定义数组时,直接赋初始值
(2)使用apply方法创建数组对象
3)案例实操
// 1. 创建一个不可变数组
// 1) new
val arr1: Array[Int] = new Array[Int](5)
// 2) 用伴生对象的apply方法
val arr2: Array[Int] = Array(1,2,3,4,5)
println("===========================")
// 2. 访问数组中元素,做读写操作
println(s"${arr1(0)} ${arr1.apply(1)} ${arr1(2)} ${arr1(3)} ${arr1(4)}" )
// println(s"${arr1(5)}")
arr1(3) = 13
arr1.update(0, 48) // arr1(0) = 48
arr2(2) = 27
println(arr1.mkString(" "))
println(arr2.mkString("-"))
// 3. 遍历所有元素
// 1) for循环,遍历索引
for( i <- 0 until arr2.length )
print(arr2(i) + "\t")
println()
for( i <- arr2.indices ) print(arr2(i) + "\t")
println()
// 2) for循环直接遍历元素
for( elem <- arr2 ) print(elem + "\t")
println()
// 3) 迭代器
val iter: Iterator[Int] = arr2.iterator
while(iter.hasNext)
print(iter.next() + "\t")
println()
// 4) foreach方法
arr2.foreach( elem => print(elem + "\t") )
println()
arr2.foreach( println )
println("===============================")
// 4. 向数组中添加元素
// 生成一个新的数组,原数组不变
val newArr1: Array[Int] = arr2.:+(20)
println(arr2.mkString(" "))
println(newArr1.mkString(" "))
val newArr2: Array[Int] = newArr1.+:(63)
println(newArr2.mkString(" "))
val newArr3 = 15 +: arr1 :+ 39 :+ 9
val newArr4 = 29 +: 15 +: newArr3
println(newArr4.mkString(" "))
6.2.2 可变数组
1)定义变长数组
val arr01 = ArrayBuffer[Any](3, 2, 5)
(1)[Any]存放任意数据类型
(2)(3, 2, 5)初始化好的三个元素
(3)ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer
2)案例实操
(1)ArrayBuffer是有序的集合
(2)增加元素使用的是append方法(),支持可变参数
// 可变数组
object Test02_ArrayBuffer {
def main(args: Array[String]): Unit = {
// 1. 创建一个可变数组
val arr1: ArrayBuffer[Int] = new ArrayBuffer[Int]()
val arr2 = ArrayBuffer(3, 56, 29)
println(arr1)
println(arr2)
// 2. 访问数组元素进行读写操作
println(arr2(1))
arr2(0) = 12
println(arr2)
println("============================")
// 3. 向数组中添加元素
// val newArr1 = arr1 :+ 59
// println(newArr1)
arr1.append(23)
println(arr1)
arr1.append(35, 71)
println(arr1)
arr1.prepend(8, 13)
println(arr1)
arr1.insert(1, 46, 92)
println(arr1)
// 符号方法的调用
println(arr2)
arr2 += 23 += 25
println(arr2)
37 +=: arr2
println(arr2)
// 4. 删除元素
val a = arr2.remove(2) // 按索引位置删除
println(arr2)
println(a)
arr2 -= 23 // 按元素值删除
println(arr2)
// 5. 增加另一个数组的所有元素
arr2.appendAll( ArrayBuffer(27,47,80) )
println(arr2)
arr2 ++=: arr1
println(arr1)
println(arr2)
// 6. 可变数组和不可变数组的转换
val arr: ArrayBuffer[Int] = ArrayBuffer(2,4,7)
val newArr: Array[Int] = arr.toArray
println(newArr.mkString("-"))
println(arr)
val buffer = newArr.toBuffer
println(buffer)
println(newArr)
7.2.3 不可变数组与可变数组的转换
说明
arr1.toBuffer //不可变数组转可变数组
arr2.toArray //可变数组转不可变数组
(1)arr2.toArray返回结果才是一个不可变数组,arr2本身没有变化
(2)arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化
7.2.4 多维数组
1)多维数组定义
val arr = Array.ofDim[Double](3,4)
说明:二维数组中有三个一维数组,每个一维数组中有四个元素
2)案例实操
定义一个五维数组并遍历
val arr = Array.ofDim[Int](2,3,4,5,6)
arr(0)(2)(1)(3)(4) = 100
arr.foreach(line1 => line1.foreach(line2 => line2.foreach(line3 => line3.foreach(line4 => line4.foreach( println)))))
arr.foreach(_.foreach(_.foreach(_.foreach(_.foreach( print)))))
6.3 Seq集合(List)
6.3.1 不可变List
(1)List默认为不可变集合
(2)创建一个List(数据有顺序,可重复)
(3)遍历List
(4)List增加数据
(5)集合间合并:将一个整体拆成一个一个的个体,称为扁平化
(6)取指定数据
(7)空集合Nil
2)案例实操
//(1)List默认为不可变集合
//(2)创建一个List(数据有顺序,可重复)
val list1:List[Int] = List(1,2,3,4)
println(list1)
//(7)空集合Nil
val list2:List[Int] = 56::23::12::45::Nil
println(list2)
//(4)List增加数据
val list3 = list1 :+ 5
println(list3)
val list4 = 21:: 64 :: list2
println(list4)
//(5)集合间合并:将一个整体拆成一个一个的个体,称为扁平化
val list5 = list1 :: list2
println(list5)
val list6 = list1 ::: list2
println(list6)
//(6)取指定数据
println(list2(0))
//(3)遍历List
list6.foreach(println)
6.3.2 可变List
1)说明
(1)创建一个可变集合ListBuffer
(2)向集合中添加数据
(3)打印集合数据
//(1)创建一个可变集合
val list1 = ListBuffer(1,2,3,50)
//(2)向集合中添加数据
list1.insert(1,5)
list1.append(30)
list1 += 20
println(list1)
//(4)修改数据
list1.update(2,50)
list1(1) = 2
println(list1)
//(5)删除数据
list1.remove(2)
list1.-(50)
list1 -= 20
println(list1)
//(3)打印集合数据
list1.foreach(println)
6.4 set集合
默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包
6.4.1不可变set
1)说明
(1)Set默认是不可变集合,数据无序
(2)数据不可重复
(3)遍历集合
2)案例实操
// (1)Set默认是不可变集合,数据无序
val set1 = Set(1,2,3,4,5,6)
println(set1)
// (2)数据不可重复
val set2 = Set(2,3,4,7,1,3)
println(set2)
// (3)遍历集合
set2.foreach(println)
for(elem <- set1) print(elem + "\t")
println()
6.4.2 可变mutable.set
1)说明
(1)创建可变集合mutable.Set
(2)打印集合
(3)集合添加元素
(4)向集合中添加元素,返回一个新的Set
(5)删除数据
2)案例实操
// (1)创建可变集合mutable.Set
val set3 = mutable.Set(1,2,3,4,5,6)
// (2)打印集合
println(set3)
// (3)集合添加元素
set3.add(25)
set3 += 31
set3 ++= set2
println(set3)
// (4)向集合中添加元素,返回一个新的Set
val set4 = set3 + 23
println(set4)
// (5)删除数据
set4.remove(2)
set4 -= 5
println(set4)
6.5 map集合
Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(**key-value)**映射
6.5.1 不可变map
1)说明
(1)创建不可变集合Map
(2)循环打印
(3)访问数据
(4)如果key不存在,返回0
2)案例实操
// (1)创建不可变集合Map
val map = Map("a" -> 5 , "b" -> 6 , "c" -> 10 , "d" -> 15)
// (2)循环打印
map.foreach(println)
println(map.keys)
println(map.values)
// (3)访问数据
println(map.get("a"))
// (4)如果key不存在,返回0
println(map.getOrElse("e",0))
6.5.2 可变map
1)说明
(1)创建可变集合
(2)打印集合
(3)向集合增加数据
(4)删除数据
(5)修改数据
2)案例实操
// (1)创建可变集合
val map2 = mutable.Map(("a",5),("b",50),("c",13),("d",32))
// (2)打印集合
for (elem <- map2) {print(elem + "\t")}
println()
// (3)向集合增加数据
map2.put("e",20)
println(map2)
// (4)删除数据
map2.remove("e")
println(map)
// (5)修改数据
map2.update("d", 12)
map2.update("e",15)
println(map2)
6.6 元组
1)说明
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
注意:元组中最大只能有22个元素。
2)案例实操
(1)声明元组的方式:(元素1,元素2,元素3)
(2)访问元组
(3)Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
//(1)声明元组的方式:(元素1,元素2,元素3)
val tuple1:(Int,String,Char) = (25,"a",'a')
//(2)访问元组
//(2.1)通过元素的顺序进行访问,调用方式:_顺序号
println(tuple1._1)
val tuple2 :(Int,(Int,String),Char,Boolean) = (25,(44,"a"),'c',true)
println(tuple2._2._1)
//(2.2)通过索引访问数据
println(tuple1.productElement(1))
//(2.3)通过迭代器访问数据
for (elem <- tuple1.productIterator) {print(elem + "\t")}
println()
6.7 集合常用函数
6.7.1 基本属性和常用操作
1)说明
(1)获取集合长度
(2)获取集合大小
(3)循环遍历
(4)迭代器
(5)生成字符串
(6)是否包含
2)案例实操
var list1 = List(1,2,3,4,5)
// (1)获取集合长度
println(list1.length)
// (2)获取集合大小
println(list1.size)
// (3)循环遍历
for (elem <- list1) {print(elem + "\t")}
println()
list1.foreach(println)
// (4)迭代器
val iterator = list1.iterator
while (iterator.hasNext) {
print(iterator.next() + "\t")
}
println()
// (5)生成字符串
println(list1.mkString(" "))
// (6)是否包含
println(list1.mkString(" ").contains(3))
6.7.2 衍生集合
1)说明
(1)获取集合的头
(2)获取集合的尾(不是头的就是尾)
(3)集合最后一个数据
(4)集合初始数据(不包含最后一个)
(5)反转
(6)取前(后)n个元素
(7)去掉前(后)n个元素
(8)并集
(9)交集
(10)差集
(11)拉链
(12)滑窗
2)案例实操
val list1 = List(1,2,3,4,5,6)
val list2 = List(4,5,6,7,8,9)
println(list1)
// (1)获取集合的头
println(list1.head)
// (2)获取集合的尾(不是头的就是尾)
println(list1.tail)
// (3)集合最后一个数据
println(list1.last)
// (4)集合初始数据(不包含最后一个)
println(list1.init)
// (5)反转
println(list1.reverse)
// (6)取前(后)n个元素
println(list1.take(3))
println(list1.takeRight(3))
// (7)去掉前(后)n个元素
println(list1.drop(3))
println(list1.dropRight(3))
// (8)并集
println(list1.union(list2))
println(list2.union(list1))
// (9)交集
println(list1.intersect(list2))
println(list2.intersect(list1))
// (10)差集
println(list1.diff(list2))
println(list2.diff(list1))
// (11)拉链
println(list1.zip(list2))
// (12)滑窗
for (elem <- list1.sliding(3)) {print(elem + "\t")}
println()
for (elem <- list1.sliding(3,2)) {print(elem + "\t")}
println()
6.7.3 集合计算初级函数
1)说明
(1)求和
(2)求乘积
(3)最大值
(4)最小值
(5)排序
2)实操
val list1 = List(3,1,5,20,16,48,4)
// (1)求和
println(list1.sum)
// (2)求乘积
println(list1.product)
// (3)最大值
println(list1.max)
// (4)最小值
println(list1.min)
// (5)排序
println(list1.sorted)
println(list1.sorted(Ordering[Int].reverse))
println(list1.sortBy(x => x))
println(list1.sortBy(x => -x))
println(list1.sortWith(_ < _))
println(list1.sortWith(_ > _))
(1)sorted
对一个集合进行自然排序,通过传递隐式的Ordering
(2)sortBy
对一个属性或多个属性进行排序,通过它的类型。
(3)sortWith
基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。
6.7.4 集合计算高级函数
1)说明
(1)过滤
遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
(2)转化/映射(map)
将集合中的每一个元素映射到某一个函数
(3)扁平化
(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作
集合中的每个元素的子元素映射到某个函数并返回新集合
(5)分组(group)
按照指定的规则对集合的元素进行分组
(6)简化(归约)
(7)折叠
2)案例实操
val list1 = List(2,1,5,6,3,4)
// (1)过滤
// 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
println(list1.filter(_ % 2 == 0))
println(list1.filter(_ % 2 == 1))
// (2)转化/映射(map)
// 将集合中的每一个元素映射到某一个函数
println(list1.map(data => data))
// (3)扁平化
val list2 = List(List(1,2,3),List(4,5,6),List(7,8,9))
println(list2.flatten)
// (4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作
// 集合中的每个元素的子元素映射到某个函数并返回新集合
val List3: List[String] = List("hello world", "hello atguigu", "hello scala")
List3.flatMap(x => x.split(" "))
// (5)分组(group)
// 按照指定的规则对集合的元素进行分组
println(list1.groupBy(data => data % 2))
println("*****************************")
// (6)简化
println(list1.reduce((data1, data2) => data1 + data2)) //等价于sum
println(list1.reduce(_ - _)) // -17 2-1-5-6-3-4
println(list1.reduceRight(_ - _)) //-1 2-(1-(5-(6-(3-4))))
// (7)折叠
println(list1.fold(10)(_ - _)) //-11 10-2-1-5-6-3-4
println(list1.foldRight(10)(_ - _)) //9 2-(1-(5-(6-(3-(4-10)))))
3)案例实操:合并两个map(key相同,value做和)
val map1 = mutable.Map("a"->1, "b"->2, "c"->3)
val map2 = mutable.Map("a"->4, "b"->5, "d"->6)
println(map1)
println(map2)
val map3 = map1 ++ map2
println(map3)
val map4 = map2.foldLeft(map1){
(map,kv) => {
val k = kv._1
val v = kv._2
map(k) = map.getOrElse(k,0) + v
map
}
}
println(map4)
6.7.5 简单WordCount
1)需求
单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
2)案例实操
val list = List(
"hello world",
"hello scala",
"hello spark",
"hello spark from scala"
)
println(list)
val list1 = list.flatMap(x => x.split(" "))
.groupBy(data => data)
.toList.map(kv => (kv._1, kv._2.length))
.sortBy( -_._2 )
.take(3)
println(list1)
6.7.6 复杂WordCount案例
1)方式一
val tupleList = List(
("Hello Scala Spark World ", 4),
("Hello Scala Spark", 3),
("Hello Scala", 2),
("Hello", 1))
val list2 = tupleList.map(kv => kv._1 * kv._2)
.flatMap(x => x.split(" "))
.groupBy(data => data)
.toList
.map(kv => (kv._1, kv._2.length))
.sortBy(-_._2)
.take(3)
println(list2)
2)方式二
val list3 = tupleList.flatMap {
kv => {
val strings: Array[String] = kv._1.split(" ")
strings.map(word => (word,kv._2))
}
}
println(list3)
println(list3.groupBy(data => data._1)
.mapValues(data => data.map(data => data._2))
.map(kv => (kv._1,kv._2.sum)))
6.8 队列
1)说明
Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
2)案例实操
val queue:mutable.Queue[String] = new mutable.Queue[String]()
queue.enqueue("a","b","c")
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
栈和队列大致相同,只不过队列是先进先出,栈是后进先出。栈中也有两个方法,push和pop
6.9 并行集合
1)说明
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
2)案例实操
def main(args: Array[String]): Unit = {
val result = (0 to 100).map{case _ => Thread.currentThread().getName}
val result2 = (0 to 100).par.map{case _ => Thread.currentThread().getName}
println(result)
println(result2)