Scala进阶之路-高级数据类型之集合的使用

时间:2021-09-05 20:11:41

            Scala进阶之路-高级数据类型之集合的使用

                               作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

  Scala 的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable 特质在 Scala 中集合有可变(scala.collection.mutable” 包下存放)和不可变(scala.collection.immutable” 包下存放)两种类型,immutable 类型的集合初始化后就不能改变了(注意与 val 修饰的变量进行区别)。

Scala进阶之路-高级数据类型之集合的使用

一.Seq  序列

1>.不可变的序列 import scala.collection.immutable._

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.function object SetOperation {
def main(args: Array[String]): Unit = {
//定义一个list
var list = List("yinzhengjie","Python","Golang","Java","Scala")
//查看list的头
print(s"list.head=====> ${list.head}\n")
//查看list的尾,尾就是出了头部的数据
print(s"list.tail=====> ${list.tail}\n")
//Nil表示为空,此时的list为空
list = Nil
print(s"list=====> ${list}\n")
/**
* "::" 操作符是将给定的头和尾创建一个新的列表。
* 注意,"::" 操作符是右结合的,如 100::200::300::Nil 相当于 100::(200::(300::Nil))
*/
var list2 = 100::200::300::list
println("list2=====>"+list2)
//“+”是拼接操作
var list3 = list2 + ("666666")
print(s"list3=====> ${list3}\n")
//“++”是将两个集合进行拼接操作,最终会返回一个新的list对象,将右边的集合的每一个元素追加在左边集合的尾部
var list4 = list2 ++ List("J","Q","K")
print(s"list4=====> ${list4}\n")
//“++:”是将右边的集合的每一个元素元素放置在左边集合的头部哟
var list5 = list2.++:(List("J","Q","K"))
print(s"list5=====> ${list5}\n")
//“+:”是将右边的集合的放置在左边集合的头部,并非是将右边的集合的每个元素放进去!
var list6 = list2.+:(List("J","Q","K"))
print(s"list6=====> ${list6}\n")
//“:+”是将右边的集合的放置在左边集合的尾部部,并非是将右边的集合的每个元素放进去!
var list7 = list2.:+(List("J","Q","K"))
print(s"list7=====> ${list7}\n")
//“::”方法是将右边集合的每个元素和左边的集合合并为一个新的集合
var list8 = list2::(List("J","Q","K"))
print(s"list8=====> ${list8}\n")
//判断list9集合中存在偶数的个数
val list9 = List(9,2,3,6,8,7,1,4,5)
val res1 = list9.count(x => x % 2 == 0)
print(s"res1=====> ${res1}\n")
//判断list9集合中偶数的值
val res2 = list9.filter(x => x % 2 == 0)
print(s"res2=====> ${res2}\n")
//对list9进行升序排序
val res3 = list9.sorted
print(s"res3=====> ${res3}\n")
//list9进行降序排序,如果我们不用负号("-"),那么默认是以升序排序
val res4 = list9.sortBy(x => -x)
print(s"res4=====> ${res4}\n")
//根据wordCount中的Int类型进行排序
val wordCount = List(("yinzhengjie",100),("Scala",80))
val res5 = wordCount.sortWith((x,y) => x._2 > y._2)
print(s"res5=====> ${res5}\n")
//指定每个分组允许的最大个数
val lis10 = List("yinzhengjie","Shell","Python","VBS","HTML","CSS","JavaScript","Golang","Java","Scala")
val res6 = lis10.grouped(3)
res6.foreach(x => print(x + "\t"))
print(s"res6=====> ${res6}\n")
/**
* 注意fold(定义初始值)(叠加的函数)
* 分析:val res7 = list11.fold(0)(_+_)
* fold(0)中的初始值为 : 0
* (_+_)是叠加的函数,左边的下划线是初始值,右边的下划线是list11中的每一个元素
* 综上所述:
* list11.fold(0)(_+_)会进行三次运算,因为list11的长度为3.
* 第一次运算,计算结果为: (0+9)
* 第二次运算的初始值是第一次计算的结果,计算结果为: (9+2)
* 第三次运算的初始值是上一次计算的结果,计算结果为: (11+3)
* 因此,我们可以得到最终的结果应该为: 14
*/
val list11= List(9,2,3)
val res7 = list11.fold(0)(_+_) //其实我们也可以这样写:val res7 = list11.fold(0)((x,y) => x + y)
print(s"res7=====> ${res7}\n")
/**
* 相信你理解了上面的fold的方法,那么下面的fold的方法你估计也再熟悉不过了,因为fold底层调用的就是foldLeft方法。
*/
var res8 = list11.foldLeft(0)(_+_)
var res9 = list11.foldLeft(0)(_-_)
print(s"res8=====> ${res8}\n")
print(s"res9=====> ${res9}\n")
/**
* 在你理解foldLeft的工作原理后,相信foldRight对你来说更是小菜一碟啦,因为foldLeft和fold都是从左往右计算
* 第一次运算,计算结果为: (9-0)
* 第二次运算的初始值是上一次计算的结果,计算结果为: (2-9)
* 第三次运算的初始值是上一次计算的结果,计算结果为: (3-(2-9))
* 因此,我们可以得到最终的结果应该为: 10
*/
var res10 = list11.foldRight(0)(_-_)
print(s"res10=====> ${res10}\n")
//反转list的值,使用reverse方法
var list12 = List("yinzhengjie","Shell","Python","Scala")
list12 = list12.reverse
print(s"list12=====> ${list12}\n")
//将列表进行聚合操作,如果列表中有字符串就直接拼接,如果全部是int类型就累加操作
val res11 = list12.reduce((x,y) => x + y)
print(s"res11=====> ${res11}\n")
//aggregate(在Spark已经把它封装为reduceByKey)可以模拟并行化集合,可以把集合打散,在非并行化底层还是调用的foldLeft
var res12 = list11.aggregate(0)(_ + _,_+_)
print(s"res12=====> ${res12}\n")
//计算出两个集合中的并集,就可以调用union方法
var list13 = List(1,3,5,7,9,0,8)
var list14 = List(8,0,7,2,4,6)
var res13 = list13.union(list14)
print(s"res13=====> ${res13}\n")
//计算交集,调用intersect方法即可
var res14 = list13.intersect(list14)
print(s"res14=====> ${res14}\n")
//计算两个集合的差集,即可以理解为不同元素
var res15 = list13.diff(list14)
print(s"res15=====> ${res15}\n")
//拉链函数,可以把两个元素集合的没有索引进行咬合,新城一个新的元组。如果一个集合中比另一个集合元素要少,那么会以较少元素个数的集合进行匹配。
var res16 = list13.zip(list14)
print(s"res16=====> ${res16}\n")
//格式化字符串操作,比如以“|”将list13中的每个元素隔开,并将结果以字符串的形式返回
var res17 = list13.mkString("|")
print(s"res17=====> ${res17}\n")
//计算集合的大小或者是长度
var res18 = list13.size
var res19 = list13.length
print(s"res18=====> ${res18}\n")
print(s"res19=====> ${res19}\n")
//对应list取切片操作,和Python语法有点像,同样也是挂前不顾后!
var list15 =List("yinzhengjie","Hadoop","Nginx","MySQL","TomCat","Httpd")
var res20 = list15.slice(1,3)
print(s"res20=====> ${res20}\n")
//对一个Int类型的结合进行求和运算
var list16 = List(1,2,3,4,5,6,7,8,9,10)
var res21 = list16.sum
print(s"res21=====> ${res21}\n")
}
} /*
以上代码执行结果如下:
list.head=====> yinzhengjie
list.tail=====> List(Python, Golang, Java, Scala)
list=====> List()
list2=====>List(100, 200, 300)
list3=====> List(100, 200, 300)666666
list4=====> List(100, 200, 300, J, Q, K)
list5=====> List(J, Q, K, 100, 200, 300)
list6=====> List(List(J, Q, K), 100, 200, 300)
list7=====> List(100, 200, 300, List(J, Q, K))
list8=====> List(List(100, 200, 300), J, Q, K)
res1=====> 4
res2=====> List(2, 6, 8, 4)
res3=====> List(1, 2, 3, 4, 5, 6, 7, 8, 9)
res4=====> List(9, 8, 7, 6, 5, 4, 3, 2, 1)
res5=====> List((yinzhengjie,100), (Scala,80))
List(yinzhengjie, Shell, Python) List(VBS, HTML, CSS) List(JavaScript, Golang, Java) List(Scala) res6=====> empty iterator
res7=====> 14
res8=====> 14
res9=====> -14
res10=====> 10
list12=====> List(Scala, Python, Shell, yinzhengjie)
res11=====> ScalaPythonShellyinzhengjie
res12=====> 14
res13=====> List(1, 3, 5, 7, 9, 0, 8, 8, 0, 7, 2, 4, 6)
res14=====> List(7, 0, 8)
res15=====> List(1, 3, 5, 9)
res16=====> List((1,8), (3,0), (5,7), (7,2), (9,4), (0,6))
res17=====> 1|3|5|7|9|0|8
res18=====> 7
res19=====> 7
res20=====> List(Hadoop, Nginx)
res21=====> 55
*/

2>.可变的序列 import scala.collection.mutable._

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.function import scala.collection.mutable.ListBuffer object ListOperationDemo { def main(args: Array[String]): Unit = {
//构建一个可变列表,初始有 3 个元素 1,2,3
val res = ListBuffer[Int](1,2,3)
print(s"res=====> ${res}\n") //创建一个空的可变列表
val res2 = new ListBuffer[Int]
print(s"res2=====> ${res2}\n") //向 res2 中追加元素,注意:没有生成新的集合
res2+= 4
res2.append(5)
print(s"res2=====> ${res2}\n") //将 res2 中的元素追加到 res 中, 注意:没有生成新的集合
res ++= res2
print(s"res=====> ${res}\n") //将 res 和 res2 合并成一个新的 ListBuffer 注意:生成了一个集合
val res3 = res ++ res2
print(s"res3=====> ${res3}\n") //将元素追加到 res 的后面生成一个新的集合
val res4 = res :+ 10
print(s"res4=====> ${res4}\n")
}
} /*
以上代码执行结果如下 :
res=====> ListBuffer(1, 2, 3)
res2=====> ListBuffer()
res2=====> ListBuffer(4, 5)
res=====> ListBuffer(1, 2, 3, 4, 5)
res3=====> ListBuffer(1, 2, 3, 4, 5, 4, 5)
res4=====> ListBuffer(1, 2, 3, 4, 5, 10)
*/

二.Set集合

1>.不可变的 Set

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.function object setOperationDemo {
def main(args: Array[String]): Unit = {
var s1 =Set(1,2,3,5)
var s2 =Set(2,3,4)
//计算交集
val res1 = s1.intersect(s2)
val res2 = s1 & s2
println(s"res1 ======> ${res1}")
println(s"res2 ======> ${res2}")
//计算并集
val res3 = s1.union(s2)
val res4 = s1 | s2
println(s"res3 ======> ${res3}")
println(s"res4 ======> ${res4}")
//计算差集
val res5 = s1.diff(s2)
val res6 = s1 &~ s2
println(s"res5 ======> ${res5}")
println(s"res6 ======> ${res6}") /**
* 查看当前使用的是哪个类
*/
println(s1.getClass)
}
} /*
以上代码执行结果如下 :
res1 ======> Set(2, 3)
res2 ======> Set(2, 3)
res3 ======> Set(5, 1, 2, 3, 4)
res4 ======> Set(5, 1, 2, 3, 4)
res5 ======> Set(1, 5)
res6 ======> Set(1, 5)
class scala.collection.immutable.Set$Set4
*/

2>.可变的 Set

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.function import scala.collection.mutable object setOperationDemo {
def main(args: Array[String]): Unit = {
/**
* 创建一个可变的 HashSet
*/
val set1 = new mutable.HashSet[Int]()
print(s"set1=====> ${set1}\n")
/**
* 向“HashSet ”中添加元素 :
* 1>.由于set的顺序是无序的,因此插入过程中可能不存在 ;
* 2>.add一般用于追加一个元素 ;
* 3>.如果想要一次性追加多个元素可以用".+="的方式添加,也可以使用“++=”操作符 ;
*/
set1.add(4)
set1 += (100,500)
set1.+=(300,200)
set1 ++= Set (1,3,5)
print(s"set1=====> ${set1}\n") /**
* 向“HashSet”中删除元素 :
* 1>.删除一个元素一般用"remove"方法 ;
* 2>.如果要删除多个元素
*/
set1 -= (100,200)
set1.-=(3,4)
set1.remove(5)
print(s"set1=====> ${set1}\n") /**
* 查看当前使用的是那个类
*/
println (set1.getClass)
}
} /*
以上代码执行结果如下 :
set1=====> Set()
set1=====> Set(1, 300, 100, 5, 3, 500, 4, 200)
set1=====> Set(1, 300, 500)
class scala.collection.mutable.HashSet
*/

三.Map映射

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.basicGrammar import scala.collection.mutable object MapDemo {
def main(args: Array[String]): Unit = {
//定义不可变的map
val map = Map[String,Int]("尹正杰" -> 18)
print(s"map=====> ${map}\n") //定义一个可变的map
val changeableMap = mutable.HashMap[String,Int]()
print(s"changeableMap=====> ${changeableMap}\n") //往可变map中添加元素,这种添加方式和Java如出一辙
changeableMap.put("尹正杰", 26 )
changeableMap += "yinzhengjie" -> 18 //当然Scala也可以使用“+=”添加元素哟
changeableMap +=(("JDK",8),("Scala",2),("Hadoop",2)) //如果想要一次性添加多个元素,每个元素有逗号分隔即可
print(s"changeableMap=====> ${changeableMap}\n") //删除map中元素
changeableMap.remove("Scala")
print(s"changeableMap=====> ${changeableMap}\n")
changeableMap.-=("Hadoop") //当然Scala也可以使用“-=”删除元素哟
print(s"changeableMap=====> ${changeableMap}\n") //获取Map中的元素
val res1 = map.get("尹正杰").get
print(s"res1=====> ${res1}\n")
val res2 = changeableMap.get("尹正杰").get
print(s"res2=====> ${res2}\n") //getOrElse方法是判断Map中是否有某个key,如果有就返回具体的值,若没有则返回我们提前设定的值
val res3 = changeableMap.getOrElse("Python",100)
val res4 = changeableMap.getOrElse("JDK",200)
print(s"res3=====> ${res3}\n")
print(s"res4=====> ${res4}\n") /**
* 扩充知识点:
* 在Scala 中Option 类型样例类用来表示可能存在或也可能不存在的值(Option 的子类有Some
* 和None)。Some 包装了某个值,None 表示没有值。
* Option 是Some 和None 的父类
* Some 代表有(多例),样例类
* None 代表没有(单例),样例对象
*/
var res5:Option[Any] = Some("yinzhengjie","python","shell","vbs")
print(s"res5=====> ${res5}\n")
//取出Option的值
val res6 = res5.get
print(s"res6=====> ${res6}\n")
//判断Option是否为空
val res7 = res5.isEmpty
print(s"res7=====> ${res7}\n") /**
* Option 的get方法返回的为Option, 也就意味着res6可能取到也有可能没取到,如果res5=None 时,
* 会出现异常情况: NoSuchElementException: None.get
*/
res5=None
val res8 = res5.isEmpty
print(s"res8=====> ${res8}\n")
// val res9 = res5.get
// print(s"res9=====> ${res9}\n")
}
} /*
以上代码执行结果如下 :
map=====> Map(尹正杰 -> 18)
changeableMap=====> Map()
changeableMap=====> Map(yinzhengjie -> 18, Hadoop -> 2, 尹正杰 -> 26, Scala -> 2, JDK -> 8)
changeableMap=====> Map(yinzhengjie -> 18, Hadoop -> 2, 尹正杰 -> 26, JDK -> 8)
changeableMap=====> Map(yinzhengjie -> 18, 尹正杰 -> 26, JDK -> 8)
res1=====> 18
res2=====> 26
res3=====> 100
res4=====> 8
res5=====> Some((yinzhengjie,python,shell,vbs))
res6=====> (yinzhengjie,python,shell,vbs)
res7=====> false
res8=====> true
*/

四.元组(Tuple

  Scala 元组将固定数量的项目组合在一起,以便它们可以作为一个整体传递。与数组或列表不同,元组可以容纳不同类型的对象,但它们也是不可变的。元组是类型Tuple1,Tuple2,Tuple3 等等。目前在Scala 中只能有22 个上限,如果您需要更多个元素,那么可以使用集合而不是元组。

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.basicGrammar object TupleDemo { def main(args: Array[String]): Unit = { //定义一个元组
val tuple = ("yinzhengjie",2018,true,Unit) //访问元素中的第一个元素,根据下标访问即可,默认是从1开始计数的
val res1 = tuple._1
print(s"res1=====> ${res1}\n") /**
* productIterator:
* 可以将一个元组转换成一个数组
* foreach:
* 可以遍历迭代器中的每一个元素
*/
tuple.productIterator.foreach(x => print(x + "\t"))
println() /**
* 对偶元组:
* 顾名思义。对偶元组指的是元组中元素的个数只有2个。
*/
val tuple1 = ("yinzhengjie",2018)
print(s"tuple1=====> ${tuple1}\n")
val res2 = tuple1.swap
print(s"res2=====> ${res2}\n") } } /*
以上代码执行结果如下:
res1=====> yinzhengjie
yinzhengjie 2018 true object scala.Unit
tuple1=====> (yinzhengjie,2018)
res2=====> (2018,yinzhengjie)
*/

五.Scala中的并行化集合

  Scala结合Hadoop中的MapReduce思想,可以模拟分布式计算,使用par方法即可。
 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.function object MyListPar {
def main(args: Array[String]): Unit = {
var list = (1 to 10).toList
println(list) /**
* 在没有开启并行化时,我们会看到执行map任务时都在主线程执行
*/
list.map(e => {
//获取当前线程名称
val name = Thread.currentThread().getName()
println(name + " : " + e)
//将listl列表中的数据乘以2
println(e * 2)
}) println("\n==============我是分割线=============\n") /**
* Scala结合Hadoop中的MapReduce思想,可以模拟分布式计算,使用par方法即可。
* 使用par方法将list对象转成并行化集合,我们会看到执行map任务时开启了多个线程执行,缺点是执行顺序不一致啦!
*/
list.par.map(e => {
//获取当前线程名称
val name = Thread.currentThread().getName()
println(name + " : " + e)
//将listl列表中的数据乘以2
println(e * 2)
})
}
} /*
以上代码执行结果如下 :
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
main : 1
2
main : 2
4
main : 3
6
main : 4
8
main : 5
10
main : 6
12
main : 7
14
main : 8
16
main : 9
18
main : 10
20 ==============我是分割线============= scala-execution-context-global-11 : 1
2
scala-execution-context-global-11 : 2
4
scala-execution-context-global-14 : 8
scala-execution-context-global-12 : 6
12
scala-execution-context-global-12 : 7
14
scala-execution-context-global-13 : 3
6
scala-execution-context-global-13 : 4
8
scala-execution-context-global-13 : 5
10
16
scala-execution-context-global-14 : 9
18
scala-execution-context-global-14 : 10
20 */

六.小试牛刀

1>.对数组的字符串进行单词统计

  不知道你是否感觉到了,大数据统计不管是MapReduce还是Spark他们最终的计算都类似于之中WordCount,因此想要学习大数据,练习写WordCount是你必经之路,我们可以对一个数组的数据进行单词统计,以及排序等操作,只需要一行就可以搞定,而且速度那是相当的快呀!下面我介绍4中单词统计的方式,其实万变不离其宗。具体代码如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.basicGrammar object WordCount { def main(args: Array[String]): Unit = {
//定义需要统计的单词
val words = Array("hello Scala","hello Spark","hello Java","Java Scala") // 实现方式一
val res1 = words .flatMap(_.split(" ")) // 对数组中的每个元素进行切分, 并进行扁平化操作
.map((_, 1)) // 将数组的每个元素转换成一个对偶元组, 元组的第二个元素为1
.groupBy(_._1) // 对集合中的所有元素进行按单词分组, 相同单词的元组分到一组
.mapValues(_.length) // 对每个key 的value 集合进行求长度操作
.toList // 将map 转换成List
.sortBy(t => - t._2) // 实现降序排列,默认是升序排列 // 实现方式二
val res2 = words .flatMap(_.split(" ")).groupBy(x => x).map(t => (t._1,t._2.length)).toList.sortBy(t => t._2) //实现方式三
val res3 = words.flatMap(_.split(" ")).map(x =>(x,1)).groupBy(x=>x._1).mapValues(t=>t.foldLeft(0)(_+_._2)) //实现方式四
val res4 = words.flatMap(_.split(" ")).map(x =>(x,1)).groupBy(x =>x._1).mapValues(t=>t.foldRight(0)(_._2 + _)).toList.sortBy(t => - t._2) print(s"res1=====> ${res1}\n")
print(s"res2=====> ${res2}\n")
print(s"res3=====> ${res3}\n")
print(s"res4=====> ${res4}\n") }
} /*
以上代码执行结果如下:
res1=====> List((hello,3), (Scala,2), (Java,2), (Spark,1))
res2=====> List((Spark,1), (Scala,2), (Java,2), (hello,3))
res3=====> Map(Spark -> 1, Scala -> 2, Java -> 2, hello -> 3)
res4=====> List((hello,3), (Scala,2), (Java,2), (Spark,1))
*/

2>.