Scala集合
一.集合简介
1.Scala集合有三大类:所有集合都扩展自Iterable特质
- Seq:序列——存放单值类型;有序,可重复
- Set:集——存放单值类型;无序,不可重复
- Map:映射——存放键值对;其中key无序,不可重复
下图显示了 scala.collection 中的所有集合,这些都是高级抽象类或特质,它们通常具有可变的和不变的实现:

2.对于所有的集合类scala又同时提供可变与不可变的版本,分别位于以下这两个包:
scala.collection.immutablescala.collection.mutable
1)Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改:类似于java中的String对象
2)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象:类似于java中StringBuilder对象
建议:在操作集合的时候,不可变用符号,可变用方法
1.可变集合
下图显示了scala.collection.immutable 中的所有集合:

注意:
1)Set、Map是Java中也有的集合
2)Seq是Java没有的,我们发现List归属到了Seq,因此这里的List就和Java不是同一个概念了
3)String也是属于IndexedSeq(虚线:隐式转换)
4)Map体系有一个SortedMap,说明Scala的Map可以支持排序
5)IndexedSeq和LinearSeq的区别:
- a)IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位
- b)LinearSeq是线性的,即有头尾的概念,这种数据结构一般是通过遍历来查找
6)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range
2.不可变集合
下图显示了scala.collection.mutable中的所有集合:

图中说明:

二.数组
Array的继承关系:
- Seq:序列——存放单值类型;有序,可重复
- IndexedSeq:通过索引来查找和定位
ArrayBuffer的继承关系:
1.不可变数组:Array
不可变数组,所以在执行添加或者删除操作的时候,会创建新的数组对象。
有两种创建不可变数组的方式:new 和 apply
1)方式1:通过new Array[ ]( )
- [Int]:是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
- (5):表示数组的大小,确定后就不可以变化
- 默认赋值:数值类型——各类0;String——null
方式1:
val arr01 : Array[Int] = new Array[Int](5)
//1.赋值
arr01(0) = 10 //注意:区别于java用的是小括号
arr01.update(0, 1) //采用方法给数组赋值
//2.遍历数组
//(1)查看数组:用指定的字符串连接数组中的元素,形成一个新的字符串
println(arr01.mkString(",")) //10,1,0,0,0
//2)普通for循环:快捷输入 itr
for (i <- arr01) {
println(i)
}
//3)迭代器.iterator
val iterator = arr01.iterator
while (iterator.hasNext){
println(iterator.next())
}
//4)增强for循环:.foreach
// arr01.foreach((i:Int)=>{println(i)})
// arr01.foreach(println(_))
arr01.foreach(println)
//3.增加元素(由于创建的是不可变数组,增加元素,其实是产生新的数组)
val newArr1 = arr01.+:(30)
println(newArr1.mkString(",")) //30,10,1,0,0,0
//在Scala语言中,如果运算符方法中包含冒号,并且冒号在后,运算顺序为从右到左
//val newArr2: Array[Int] = arr01 +: 30 编译报错
val newArr3: Array[Int] = 30 +: arr01
println(newArr3.mkString(",")) //30,10,1,0,0,0
//同理冒号在左边:30添加到末尾
val newArr4 = arr01 :+ 30
println(newArr4.mkString(",")) //10,1,0,0,0,30
2)方式2:通过 Array(1, 2)
- 在定义数组时,直接赋初始值
- 使用apply方法创建数组对象
方式2:
val arr2 : Array[Int] = Array(1,2,3,4,5)
2.可变数组:ArrayBuffer
可变数组,在执行添加或者删除操作的时候,不会创建新的数组对象,直接在源数组上进行操作:
- 包名:
scala.collection.mutable.ArrayBuffer
两种方式:new 和 apply
//方式1:
val arr0: ArrayBuffer[Int] = ArrayBuffer(1,2,3)
//方式2:
val arr = new ArrayBuffer[Int]()
println(arr.length) //0
//1.向数组中添加元素:append方法(),支持可变参数
//建议:在操作集合的时候,不可变用符号,可变用方法
// arr(0) = 20 运行出错:数组中并没有元素
arr.append(10)
arr.insert(1,20)
// arr.insert(5,20) 运行出错:数组长度没有到达索引5位置
println(arr.mkString(",")) //10,20
// arr.update(0,10)
//2.访问数组中的元素 对数组中的元素进行修改
arr(0) = 20
arr.update(1,30)
println(arr.mkString(",")) //20,30
//3.注意:并不是数组是可变的,在操作数组对象的时候,就不能创建新的数组对象了
val newArr = arr :+ 40
println(newArr.mkString(",")) //20,30,40
println(arr.mkString(",")) //20,30
//4.删除数组中的元素
arr.append(40,50,60)
val num: Int = arr.remove(1)
println(num)
arr.remove(1,2)
println(arr.mkString(","))
3.不可变与可变数组的转换
arr1.toBuffer//不可变数组转可变数组,返回结果才是一个可变数组,arr1本身没有变化arr2.toArray//可变数组转不可变数组,返回结果才是一个不可变数组,arr2本身没有变化
//可变数组转换为不可变数组
val arr2: ArrayBuffer[Int] = ArrayBuffer(1,2,3)
val newArr2: Array[Int] = arr2.toArray
//不可变数组转换为可变数组
val buffer: mutable.Buffer[Int] = newArr2.toBuffer
4.多维数组
val arr = Array.ofDim[Double](3,4)
//对二维数组进行遍历
for (i <- 0 until arr.length;j <- 0 until (arr(i).length)){
println(arr(i)(j))
}
说明:二维数组中有三个一维数组,每个一维数组中有四个元素
三.Seq 列表:List
List继承关系:
- Seq:序列——存放单值类型;有序,可重复
- LinearSeq:线性的,即有头尾的概念,这种数据结构一般是通过遍历来查找
ListBuffer的继承关系:
1.不可变 List
1)创建List集合:因为List是抽象的,只能通过apply方式创建
2)添加元素: +:、:+、
3)扁平化:三冒号
4)Nil 表示空集合
5)获取元素、遍历元素
object TestList {
def main(args: Array[String]): Unit = {
//1.创建List集合:因为List是抽象的,只能通过apply方式创建
val list = List(1, 2, 3, 4, 5)
//2.向集合中添加元素:不可变集合——用符号
val newList1 = list.+:(20) //20 +: list
val newList2 = list.:+(20) //list :+ 20
println(newList1) //List(20, 1, 2, 3, 4, 5)
println(newList2) //List(1, 2, 3, 4, 5, 20)
//3.向集合中添加元素:双冒号、三冒号
val list2 : List[Int] = List(1,2,3,4)
val list3 : List[Int] = List(5,6)
//双冒号:List整体作为元素添加
val newList3: List[Any] = list2 :: list3
println(newList3) //List(List(1, 2, 3, 4), 5, 6)
//三冒号:List扁平化处理后添加
val newList4: List[Any] = list2 ::: list3
println(newList4) //List(1, 2, 3, 4, 5, 6)
//4.在Scala语言中,List()===>Nil 表示空集合
val newList5: List[Int] = 10 :: 20 :: 30 :: 40 :: Nil
println(newList5) //List(10, 20, 30, 40)
//5.获取元素
println(list(4))
//6.遍历
list.foreach(println)
}
}
2.可变 ListBuffer
//0.需要导入包
import scala.collection.mutable.ListBuffer
object TestListBuffer {
def main(args: Array[String]): Unit = {
//1.创建可变集合对象
// 方式1:new 方法
val list0: ListBuffer[Int] = new ListBuffer[Int]()
// 方式2:apply 方法
val list: ListBuffer[Int] = ListBuffer(1,2,3)
//2.向集合中添加元素
// 建议:可变用方法,不可变用符号
list.append(10)
list.insert(1,20)
println(list) //ListBuffer(1, 20, 2, 3, 10)
//3.修改元素
list(0) = 30
list.update(0,30)
println(list) //ListBuffer(30, 20, 2, 3, 10)
//4.删除
list.remove(1,2)
println(list) //ListBuffer(30, 3, 10)
val list2: ListBuffer[Int] = ListBuffer(10,20,30,40)
// 1) - 方法是对原ListBuffer的克隆后进行元素的删除操作,返回一个新的值,原值未变
val anotherList = list2.-(20)
println(list2) //ListBuffer(10, 20, 30, 40)
println(anotherList) //ListBuffer(10, 30, 40)
// 2) -= 方法是对原ListBuffer直接进行操作
list2.-=(30)
println(list2) //ListBuffer(10, 20, 40)
}
}
四.Set 集合
默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包
注意区别:
- Seq:序列——存放单值类型;有序,可重复
- Set:集——存放单值类型;无序,不可重复
- Map:映射——存放键值对;其中key无序,不可重复
1.不可变Set
特点:不可变、数据无序、不可重复
//(1)Set默认是不可变集合,数据无序,不可重复
val set: Set[Int] = Set(1, 2, 3, 4, 5, 3, 4)
println(set) //Set(5, 1, 2, 3, 4)
//2)添加元素
val set1 = set.+(20)
println(set1) //Set(5, 20, 1, 2, 3, 4)
//3)遍历
set.foreach(println)
2.可变 mutable.Set
需要引用 scala.collection.mutable.Set 包
//(1)创建可变集合
val set2 = mutable.Set(1, 2, 3, 4, 5, 3, 4)
//(2)集合添加元素
set2.add(20)
set2 += 30
println(set2) //Set(30, 1, 5, 2, 20, 3, 4)
//(3)删除元素
set2-=(5)
set2.remove(4)
println(set2) //Set(30, 1, 2, 20, 3)
//(4)向集合中添加元素,返回一个新的Set
val ints = set2.+(9) //override def + (elem: A): This = clone() += elem
println(ints) //Set(30, 9, 1, 2, 20, 3)
println(set2) //Set(30, 1, 2, 20, 3)
//5)遍历、打印
set2.foreach(println)
println(set2.mkString(",")) //30,1,2,20,3
五.Map集合
Map:映射——存放键值对;其中key无序,不可重复。
Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射。
1.不可变 Map
//1.创建不可变map集合 底层就是HashMap
val map: Map[String, Int] = Map("a" -> 1, "b" -> 2, "c" -> 3)
//2.获取keys\values\value
for (key <- map.keys ) {
//在Scala语言中,为了避免空指针异常,如果获取的内容,有可能为空,则可以用Option表示
//当Map中有该key,则get(key)返回 Some(value),否则返回None
print(key + "=" + map.get(key) + "\t") //a=Some(1) b=Some(2) c=Some(3)
}
println(map.get("d")) //None
println(map.getOrElse("d", 0)) //0
for (value <- map.values ) {
print(value + "\t") //1 2 3
}
//3.添加元素,形成一个新的Map
val map1 = map.+("d" -> 5)
println(map.mkString(",")) //a -> 1,b -> 2,c -> 3
println(map1.mkString(",")) //a -> 1,b -> 2,c -> 3,d -> 5
在Scala语言中,为了避免空指针异常,如果获取的内容,有可能为空,则可以用Option表示。
当调用map.get方法的时候,返回的Option类型数据,Option有两个子类型,一个Some,另一个None:
- 当Map中有该key,则get(key)返回 Some(value)
- 否则返回None
- 如果想直接取值,而又不想取到None则可以使用:
map.getOrElse(key, 默认值)
2.可变 mutable.Map
//可变map集合
val map: mutable.Map[String, Int] = mutable.Map("a"->1,"b"->2,"c"->3)
//建议:可变用方法,不可变用符号
//添加
map.put("d",4)
map.+=("e"->4)
//删除元素
map.remove("d")
map.-=("b")
//修改map集合中元素
map.update("a",20)
map("a") = 30
map.foreach(println)
六.元组 Tuple
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
注意:元组中最大只能有22个元素
object TestTuple {
def main(args: Array[String]): Unit = {
//(1)声明元组的方式:(元素1,元素2,元素3)
//数据类型:Int\String\(参数类型)=>返回值类型\ =>(返回值类型) \ (类型...)
val tuple: (Int, String, Boolean) = (5, "zhangsan", true)
//(2)访问元组
//(2.1)通过元素的顺序进行访问,调用方式:_顺序号
println(tuple._1)
println(tuple._2)
println(tuple._3)
//(2.2)通过索引访问数据
println(tuple.productElement(0))
//(2.3)通过迭代器访问数据
for (elem <- tuple.productIterator) {
println(elem)
}
//(3)Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
val map: Map[String, Int] = Map("a"->1, "b"->2, "c"->3)
val map1: Map[String, Int] = Map(("a",1), ("b",2), ("c",3))
map.foreach(tuple=>{println(tuple._1 + "=" + tuple._2)})
}
}
七.队列 Queue
Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
Queue继承关系:
val que = new mutable.Queue[String]()
que.enqueue("a", "b", "c")
println(que) //Queue(a, b, c)
println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
println(que) //Queue()
八.集合常用函数
1.基本属性和常用操作
(1)获取集合长度
length
(2)获取集合大小
size
(3)循环遍历
foreach
(4)迭代器
iterator
(5)生成字符串
mkString
(6)是否包含
contains
2.衍生集合
(1)获取集合的头
head
(2)获取集合的尾(不是头的就是尾)
tail
(3)集合最后一个数据
last
(4)集合初始数据(不包含最后一个)
init
(5)反转
reverse
(6)取前(后)n个元素:从0到n——n>=1
take|takeRight
(7)去掉前(后)n个元素
drop|dropRight
(8)并集
union
(9)交集
intersect
(10)差集
diff
(11)拉链
zip
注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
(12)滑窗:x滑窗大小,y每次滑几步
sliding(x, y)
3.集合计算初级函数
(1)求和
sum
(2)求乘积
product
(3)最大值
max
(4)最小值
min
(5)排序
sorded|sortBy|sortWith
排序:
- 1)sorted
- 对一个集合进行自然排序(升序),通过传递隐式的Ordering
- 2)sortBy
- 对一个属性或多个属性进行排序,通过它的类型。
- 3)sortWith
- 基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑
Seq是有序集合,它才有排序方法,Map、Set是无序集合,需要转换为Seq才能进行下一步排序。
//排序
val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
println(list.sorted) //升序
// 1)按照元素大小排序
//如果函数参数和函数体是一致的情况,那么不能简化
println(list.sortBy(x => x)) //升序
// 2)按照元素的绝对值大小排序
println(list.sortBy(x => x.abs)) //绝对值 + 升序
// 3)按元素大小升序排序
println(list.sortWith((x, y) => x < y))
// 4)按元素大小降序排序
println(list.sortWith((x, y) => x > y))
注意:如果函数参数和函数体是一致的情况,那么不能简化
println(list.sortBy(x => x))
4.集合计算高级函数
(1)过滤 filter(函数:指定过滤条件)
遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
(2)转换/映射 map
(3)扁平化 flatten :::
将集合中元素由整体转换为个体的过程
(4)扁平映射 flatMap
先映射再进行扁平化处理
(5)分组 gruopBy
按照一定的分组规则,将集合中的元素放到不同的组中
(6)简化|规约 reduce
>对集合内部元素之间进行聚合
>reduce 聚合的数据类型一致
底层调用的是reduceLedft
函数参数类型必须一致
>reduceLeft|reduceRight 聚合的数据类型可以不一致
(7)折叠 fold
>对外部元素和集合内部元素之间进行聚合
>fold 聚合的数据类型一致
>foldLeft|foldRight 聚合的数据类型可以不一致
object TestFunction {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
//(1)过滤 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
//例:对List集合进行遍历,将偶数取出,放到新的集合中去
// 初始版: list.filter((i: Int) => { i % 2 == 0 })
println(list.filter(_ % 2 == 0)) // List(2, 4, 6, 8)
//(2)转化/映射(map) 将集合中的每一个元素映射到某一个函数
// list.map((i:Int) => {i*2})
println(list.map(_ * 2)) // List(2, 4, 6, 8, 10, 12, 14, 16, 18)
//(3)扁平化 :::
val nestedList : List[List[Int]] = List(List(1,2,3),List(4,5,6),List(7,8,9))
println(nestedList.flatten) //List(1, 2, 3, 4, 5, 6, 7, 8, 9)
//(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作 集合中的每个元素的子元素映射到某个函数并返回新集合
val strings: List[String] = List("hello world","hello China","hello beauty")
// strings.flatMap((s:String)=>s.split(" "))
println(strings.flatMap(_.split(" "))) //List(hello, world, hello, China, hello, beauty)
//等价于
//a)先对集合中的元素进行转换 字符串---->数组
val splitList: List[Array[String]] = strings.map(_.split(" "))
for (arr <- splitList ) {
println(arr.mkString(","))
}
//b)将集合中的元素由整体 --->个体
val flatList: List[String] = splitList.flatten
println(flatList)
//(5)分组(groupBy) 按照指定的规则对集合的元素进行分组
//按照是否为偶数分组 : list.groupBy((i:Int)=>i%2)
val map: Map[Int, List[Int]] = list.groupBy(_ % 2)
println(map) //Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8))
val nameList: List[String] = List("liuyi","zhangsan","zhaoliu","lisi")
//按照首字母分组
val map2: Map[Char, List[String]] = nameList.groupBy(_.charAt(0))
println(map2) //Map(z -> List(zhangsan, zhaoliu), l -> List(liuyi, lisi))
//6)规约:
//reduce 函数参数类型必须一致; 底层调用的是reduceLedft
println(list.reduce((a: Int, b: Int) => {
a + b
})) //45
//reduceLeft 函数参数类型可以不一致
// reduceLeft[B >: A]
println(list.reduceLeft(_-_)) //-43
//reduceRight 函数参数类型可以不一致
println(list.reduceRight(_-_)) //5
//7)折叠 集合外元素和集合内部元素进行聚合
val res: Int = list.fold(10)(_+_)
println(res) //55
println(list.foldLeft(10)(_ + _)) //55
println(list.foldRight(11)(_ - _)) //-6
}
}
具体运行原则:
- reduceLeft 和 foldLeft:
override /*TraversableLike*/
def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B =
if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
else tail.foldLeft[B](head)(op)
override /*TraversableLike*/
def foldLeft[B](z: B)(@deprecatedName('f) op: (B, A) => B): B = {
var acc = z
var these = this
while (!these.isEmpty) {
acc = op(acc, these.head)
these = these.tail
}
acc
}
- reduceRight:
override /*IterableLike*/
def reduceRight[B >: A](op: (A, B) => B): B =
if (isEmpty) throw new UnsupportedOperationException("Nil.reduceRight")
else if (tail.isEmpty) head
else op(head, tail.reduceRight(op))
- foldRight:
override def foldRight[B](z: B)(op: (A, B) => B): B =
reverse.foldLeft(z)((right, left) => op(left, right))
5.案例
案例1:两个map集合之间的合并
def main(args: Array[String]): Unit = {
val map1: mutable.Map[String, Int] = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
val map2: mutable.Map[String, Int] = mutable.Map("a"->4, "b"->5, "d"->6)
val result: mutable.Map[String, Int] = map1.foldLeft(map2) {
//map表示map2,kv表示map1中的每一个元素
(map, kv) => {
val k: String = kv._1
val v: Int = kv._2
//根据map1中元素的key,到map2中获取value
map(k) = map.getOrElse(k, 0) + v
map
}
}
println(result) //Map(b -> 7, d -> 6, a -> 5, c -> 3)
}
案例2:WordCount
需求:单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
def main(args: Array[String]): Unit = {
// 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello")
// 1) 将每一个字符串转换成一个一个单词
val wordList = stringList.flatMap(a => a.split(" "))
println(wordList) //List(Hello, Scala, Hbase, kafka, Hello, Scala, Hbase, Hello, Scala, Hello)
// 2) 将相同的单词放置在一起
val wordToMap = wordList.groupBy(i=>i)
println(wordToMap) //Map(Hello -> List(Hello, Hello, Hello, Hello),......
// 3) 对相同的单词进行计数
val wordAndCount: Map[String, Int] = wordToMap.map { kv => (kv._1, kv._2.size) }
// 4) 对计数完成后的结果进行排序(降序)
val sortList: List[(String, Int)] = wordAndCount.toList.sortWith {
(tuple1, tuple2) => tuple1._2 > tuple2._2
}
// 5) 对排序后的结果取前3名
val result: List[(String, Int)] = sortList.take(3)
println(result) //List((Hello,4), (Scala,3), (Hbase,2))
}
九.并行集合
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
案例实操
def main(args: Array[String]): Unit = {
val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}
val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}
println(result1) //Vector(main, main, main,...
println(result2) //ParVector(ForkJoinPool-1-worker-5, ForkJoinPool-1-worker-5,...
}
本文详细介绍了Scala的集合,包括可变与不可变集合,如Array、ArrayBuffer、List、Set、Map以及元组、队列等。讨论了数组的创建、转换,以及List、Set、Map的操作,并提及了并行集合的概念。
1054

被折叠的 条评论
为什么被折叠?



