Scala中的集合
一、Scala集合基础
说明:
- Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质;
- 对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包 :
- 不可变集合:scala.collection.immutable ;
- 可变集合: scala.collection.mutable 。
- Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象;
- 可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象;
- 建议:在操作集合的时候,不可变用符号,可变用方法 。
二、不可变集合
不可变集合继承图:
说明:
- Set、Map是Java中也有的集合 ;
- Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了 ;
- 前面的for循环有一个 1 to 3,就是IndexedSeq下的Range;
- String也是属于IndexedSeq ;
- 经典的数据结构比如Queue和Stack被归属到LinearSeq(线性序列) ;
- Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序 ;
- IndexedSeq和LinearSeq的区别:
- IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位 ;
- LinearSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找。
三、可变集合
可变集合继承图:
四、数组
4.1 不可变数组
数组定义;
- 第一种,例如:val arr1 = new Array[Int](10)
- [Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
- (10),表示数组的大小,确定后就不可以变化。
- 第二种,例如:val arr1 = Array(1, 2)
- 在定义数组时,直接赋初始值
- 使用apply方法创建数组对象
代码示例:
def main(args: Array[String]): Unit = { //创建数组 val array = new Array[Int](4) //查看数组,可以看到数组的默认值都是0 println(array.mkString(",")) //数组赋值 array(3) = 10 //采用update方法给数组赋值,格式:update(index,value) array.update(0,1) println(array.mkString(",")) //普通遍历数组 for (i <- array){ print(i + " ") } println() //简化遍历 array.foreach(println(_)) //因为只有一个参数,可以在把_也简化 array.foreach(println) //增加元素 //因为是不可变数组,故增加元素就是产生新的数组 val array2: Array[Int] = array :+ 5 array2.foreach(println) }
//调用apply方法创建数组 val array3:Array[Int] = Array(1,2,3) array3.foreach(println)
4.2 可变数组
说明:
- 格式,如:val arr01 = ArrayBuffer[Any](3, 2, 5) ;
- ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer ;
- ArrayBuffer是有序的集合 ;
- 增加元素使用的是append方法(),支持可变参数 。
代码示例:
def main(args: Array[String]): Unit = { val buffer = ArrayBuffer[Any](1, 2, 3, "hello") //遍历 buffer.foreach(println) println("------------------------") //追加数据 buffer += 4 //向数据最后添加数据 buffer.append(5,6) //像数组指定位置增加数据 insert(index,Any*) buffer.insert(0,7,8) //修改第一个元素的值 buffer(0) = 10 buffer.foreach(println) }
4.3 不可变数组和可变数组的转换
说明;
arr2.toArray:返回结果才是一个不可变数组,arr2本身没有变化
arr1.toBuffer:返回结果才是一个可变数组,arr1本身没有变化 。
4.4 多维数组
例如;定义一个三行四列的数组:
val arr = Array.ofDim[Double](3,4)
五、List列表
5.1 不可变List
说明:
- List默认为不可变集合 ;
- 集合间合并:将一个整体拆成一个一个的个体,称为扁平化 ;
- 构造列表的两个基本单位是 Nil 和 ::
- 可以使用 ::: 运算符或 List.:::() 方法或 List.concat() 方法来连接两个或多个列表;
- 可以使用 List.fill() 方法来创建一个指定重复数量的元素列表,如List.fill(10)(2),重复2十次;
- List.tabulate() 方法是通过给定的函数来创建列表。方法的第一个参数为元素的数量,可以是二维的,第二个参数为指定的函数,我们通过指定的函数计算结果并返回值插入到列表中:
object Test { def main(args: Array[String]) { // 通过给定的函数创建 6 个元素,n是从0开始计算的 val squares = List.tabulate(6)(n => n * n) println( "一维 : " + squares ) // 创建二维列表 val mul = List.tabulate( 4,5 )( _ * _ ) println( "多维 : " + mul ) } }
- List.reverse 用于将列表的顺序反转;
- 为列表预添加元素:如
val y = 2 +: x
- 在列表开头添加元素
val y = 2 :: x
- 将列表的所有元素添加到 StringBuilder,格式:def addString(b: StringBuilder)
- 将列表的所有元素添加到 StringBuilder,并指定分隔符,格式:def addString(b: StringBuilder, sep: String)
- 通过列表索引获取元素:格式:def apply(n: Int)
- 检测列表中是否包含指定的元素,格式:def contains(elem: Any): Boolean
- 将列表的元素复制到数组中:def copyToArray(xs: Array[A], start: Int, len: Int): Unit
- 去除列表的重复元素,并返回新列表:def distinct: List[A]
- 丢弃前n个元素,并返回新列表:def drop(n: Int): List[A]
- 丢弃最后n个元素,并返回新列表:def dropRight(n: Int): List[A]
- 从左向右丢弃元素,直到条件p不成立:def dropWhile(p: (A) => Boolean): List[A]
- 检测列表是否以指定序列结尾:def endsWith[B](that: Seq[B]): Boolean
- 更多方法查看官网API:Scala Standard Library 2.12.15 - scala.collection.immutable.List (scala-lang.org)
示例代码:
def main(args: Array[String]): Unit = { //默认为不可变集合 val list:List[Int] = List(1,2,3,4,3)
//等价于 val list = 1::2::3::4::3::Nil //增加数据 val list1 = 5::list //在列表后添加元素 val list2 = list1 :+2 //在列表前添加元素 val list3 = 0 +: list2 //两个列表合并 val list4 = list2 ::: list3 //遍历输出 list4.foreach(println) }
5.2 可变ListBuffer
def main(args: Array[String]): Unit = { //使用apply方法,创建一个ListBuffer val list = ListBuffer(1,2,3,4) //向集合添加元素 list += 5 list.append(6) //insert(index,value*) list.insert(6,7,8) list.foreach(print) //apply:根据列表索引获取元素 // println(list.apply(3)) println("\n-----------------") //修改列表元素 list(0) = 0 //update(index,value) list.update(1,10) list.foreach(print) println("\n-----------------") //删除指定数据 list -= 10 list.foreach(print) println("\n-----------------") //删除指定位置的数据 list.remove(1) list.foreach(print) } }
六、Set集合
默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包
6.1 不可变Set
说明:
- Set默认是不可变集合,数据无序 ;
- 数据不可重复。
//输出的数据无序 val set = Set(1,2,3,4,5) set.foreach(print)
6.2 可变mutable.Set
def main(args: Array[String]): Unit = { val set = Set(1,2,3,4,5) set.foreach(print) println("\n---------------------") //创建可变Set集合 val set2 = mutable.Set(2,4,1,5,6) //添加元素并返回一个新的Set集合 val set3 = set2 .+(0) //删除数据 set3 -= 6 set3.foreach(print) println("\n---------------------") //使用分隔符将列表所有元素作为字符串显示 println(set3.mkString(",")) }
七、Map结合
Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射
7.1 不可变Map
def main(args: Array[String]): Unit = { //创建不可变Map集合 val map = Map(1 ->"a",2->"b",3->"c",4->"d") println(map) for (elem <- map.keys){ //使用get访问map集合的数据,会返回特殊类型Option(选项):有值(Some),无值(None) //例:map.get(elem) 返回的结果为:1 = some(a) println(elem + "=" + map.get(elem).get) } //使用getOrElse方法,查询map中是否有key值5,如果不存在返回0; //如果存在则返回其value值 println(map.getOrElse(5,0)) }
7.2 可变Map
def main(args: Array[String]): Unit = { //创建不可变Map集合 val map = Map(1 ->"a",2->"b",3->"c",4->"d") println(map) for (elem <- map.keys){ //使用get访问map集合的数据,会返回特殊类型Option(选项):有值(Some),无值(None) //例:map.get(elem) 返回的结果为:1 = some(a) println(elem + "=" + map.get(elem).get) } //使用getOrElse方法,查询map中是否有key值5,如果不存在返回0; //如果存在则返回其value值 println(map.getOrElse(5,0)) //创建不可变Map集合 val map2 = mutable.Map(1 ->"a",2->"b",3->"c",4->"d") //向集合添加数据 map2 += (5->"e") //使用put方法,若集合中存在该key值,则更新其value //若不存在,则添加key-value map2.put(1,"x") map2.put(6,"z") //修改数据 map2.update(1,"a") println(map2) }
八、元组
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
注意:元组中最大只能有22个元素。
说明:
- 声明元组的方式:(元素1,元素2,元素3) ;
- Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶 。
def main(args: Array[String]): Unit = { //声明元组的方式:(元素1,元素2,元素3) val tuple:(Int,String,Boolean) = (10,"hello",true) //访问元组,调用方式: _顺序号 //注意:顺序号从1开始 println(tuple._2) //hello //通过索引访问元素 //注意:索引从0开始 println(tuple.productElement(0)) //通过迭代器访问数据 for(x <- tuple.productIterator){ println(x) } }
九、队列
Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
def main(args: Array[String]): Unit = { //创建一个队列 val q = mutable.Queue[String]() //入队 q.enqueue("a") q.enqueue("b") //出队 q.dequeue() println(q) }
十、并行集合
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
1、将普通集合转为并行集合
//格式:arr.par (1 to 5).foreach(println(_)) //转为并行集合 (1 to 5).par.foreach(println(_))
2、获取到参与并行计算的线程
(0 to 10000).collect{case _ => Thread.currentThread.getName}.distinct (0 to 10000).par.collect{case _ => Thread.currentThread.getName}.distinct
3、fold(折叠)在并行计算中
//fold说明: //fold方法需要传入两个参数,第一个参数是初始值,第二个参数是一个函数,函数也需要两个参数 //0作为第二个函数参数z的第一个值,根据这个函数体累加numbers集合中的元素 val numbers = List(5, 4, 8, 6, 2) numbers.fold(0) { (z, i) => z + i } // result = 25
注意:arr.fold(0)(_+_)与reduce(聚合函数)的区别是有初始值,当初始值为0,效果一样
arr.par.fold:在并行计算要注意!几个线程就会加进去几个初始值