你好,游客 登录
背景:
阅读新闻

第5章 Scala基本数据结构---Array

[日期:2021-09-26] 来源:  作者: [字体: ]

本文来自艾叔编著的《零基础快速入门Scala》免费电子书,添加文末艾叔微信,获取完整版的PDF电子书

第5章  Scala基本数据结构

3章介绍了Scala的基本数据类型,它们就相当于盖房子中的水泥、沙子、钢筋,是最基础的元素。本章介绍Scala的基本数据结构,它们就相当于盖房子中的一个个构件,如墙板、楼梯、预制梁等等。一个Scala程序离不开:基本数据类型+基本数据结构+基本控制结构这三种元素。其中基本数据结构对代码的质量、程序性能有着非常重要的影响。

5.1  Array

Array表示定长数组。

1. 声明Array

例子代码如下。

scala> val nums = new Array[Int](10)

输出如下。

nums: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

& Array后面的Int表示数组元素类型,[Int]表示数组的每个元素都是Int

& (10),表示数组有10个元素

如果不指定初始值,Int类型元素的初始值是0String类型元素的初始值是null

注意:Array前面要有new,如果没有new,括号里的10就不再表示Array的元素个数,而变成了初始值了,代码如下。

scala> val nums = Array[Int](10)

输出如下,此时的Array只有1个元素,其初始值为10

nums: Array[Int] = Array(10)

2. Array赋初始值

有时需要在声明Array时,对Array赋初始值,例如,声明一个4元素数组,初始值分别为0123,例子代码如下。

scala> val nums = Array[Int](0, 1, 2, 3)

Array赋初始值时,前面不需要new

Scala可以根据Array初始值进行类型推断,例如上面的例子,可以根据1234推断每个元素的类型是Int,因此,代码可以写成下面这样:

scala> val nums = Array(0, 1, 2, 3)

输出如下,可以看到Scala推断Array中每个元素的类型为Int

nums: Array[Int] = Array(0, 1, 2, 3)

如果初始值的类型不一样,例如:val nums = Array(0,1,2)Scala推断Array中每个元素类型为Any

3. 遍历Array

可以有多种方法,遍历Array,例如forforeach等,下面展示用不同的方法遍历Array的每个元素,并打印输出。

1for循环

采用下标+for来遍历Array中的每个元素,其中nums.length返回数组元素个数,使用数组名nums(下标)来表示每个数组元素,下标的编号从0开始,到nums.length-1结束。

val nums = Array(1, 2, 3, 4)

for(i <- 0 until nums.length){

  println(nums(i))

}

采用for直接遍历Array中的每个元素,访问的顺序和下标是一样的。

val nums = Array(1, 2, 3, 4)

for(n <- nums){

  println(n)

}

2foreach

val nums = Array(1, 2, 3, 4)

nums.foreach(println)

Array对象中自带foreach方法,可以遍历每个元素,foreach中传入println函数,可以打印每个元素。

4. 修改Array的值

Array中每个值是可以修改的,例如将Array中每个元素乘以2,代码如下。

val nums = Array(1, 2, 3, 4)

for(i <- 0 until nums.length){

  nums(i) = 2*nums(i)

}

例子代码中,nums���明的是val,表示nums是不可以再被赋值,但nums因为numsArraynums中的每个元素是可以被赋值修改的。

5. 打印Array的值(mkString,而不是toString

可以使用mkStringArray中每个元素转换成String,所有元素最后拼接成一个String,然后使用print输出,例子代码如下:

scala> print(nums.mkString)

注意,这里使用的是mkString,而不是toString,如果使用toString,代码如下:

scala> print(nums.toString)

输出如下,输出的不是每个元素的值,而是nums这个引用的文本表示。

[I@738ed8f5

6. map方法

map依次对Array中的每个元素进行操作,将操作结果放入一个新的Array(每个元素对应一个操作结果),最后返回新的Array

那么,对每个元素进行什么样的操作呢?这是由传入map的函数决定。map定义如下,这个定义很复杂,在这里,只关注最重要的部分:map的传参和返回值。

def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[Array[Int],B,That]): That

map的传参:(f: Int => B)f是一个函数,f的输入参数是Array[Int]中的每个元素,类型是Int,如果Array中每个元素是String,那么f的输入参数就是Stringf的返回值是BB可以是其它数据类型,例如String,不一定要和原来Array中元素的类型相同,map最后的返回值就是:Array[B]

map的例子代码如下,它将nums的每个元素乘以2,返回一个新的Array

scala> val newNums = nums.map(2*_)

例子代码中,传入map的函数f2*_,其中_表示nums中的元素,每个元素乘以2,作为新的操作结果,每个元素对应一个操作结果,新的操作结果按序,构成一个新的Array,赋值给newNums

因此,map操作并没有修改原Array的值,而是新建了一个Array

如果涉及每个元素的变换操作,可以考虑使用map

 

7. filter操作

filter可以对Array中的元素按条件进行筛选,符合条件的元素将被留下来,组成一个新的Array,并返回。

nums.filter的方法定义如下,filter的参数p是一个函数,p的参数是Int,和nums数组中的元素类型一致,p返回值是Boolean,如果p返回true,则保留该元素,如果为false,则放弃该元素。最后,所有保留下来的元素组成一个新的Array并返回。

def filter(p: Int => Boolean): Array[Int]

filter使用的例子代码如下,返回nums数组中所有大于2的元素。其中,p_>2_表示nums中的每个元素,如果>2,则p返回true,反之则返回false,最后所有>2的元素保留下来,组成一个新Array,赋值给filterNums

scala> val nums = Array(1,2,4,5,7)

scala> val filterNums = nums.filter(_>2)

filterNums: Array[Int] = Array(4, 5, 7)

8. flatMap操作

map是一对一的操作,一个输入元素对应一个输出结果。有的时候,需要一对多,例如,将多个字符串分割成单词,获得所有单词的集合,这个时候map无法实现,可以使用flatMap来实现。

例子代码如下,首先声明一个strListString数组,包含3String

scala> val strList = Array("Hello World", "How are you", "What is your name")

strList: Array[String] = Array(Hello World, How are you, What is your name)

使用faltMap,对每个String进行分割(按照空格分割),分割后,第一个元素得到2个单词HelloWorld,第二个元素得到3个单词Howareyou,第三个元素得到4个单词Whatisyourname,所有的单词放到一个新的Array中,一共9个元素,而原来的strList只有3个元素。因此,flatMap可以实现一对多的操作。

其中s表示strList中的每个元素,类型是String,操作是split(“ ”),即按照splits进行分割后的结果,返回值是Array[String]

scala> val wordList = strList.flatMap(s=>s.split(" "))

wordList: Array[String] = Array(Hello, World, How, are, you, What, is, your, name)

如果使用map替换flatMap,得到的结果如下,strList的每个元素被分割后,构成一个独立的Array[String],因此,map前后的元素个数是相同的,是一对一,而不像flatMap,是所有的元素构成一个Array

scala> val wordList = strList.map(s=>s.split(" "))

wordList: Array[Array[String]] = Array(Array(Hello, World), Array(How, are, you), Array(What, is, your, name))

9. reduce操作

reduce可以对Array进行汇总之类的操作,例如,求Array所有元素的的和,或者拼接Array中的所有元素等。

声明一个Array[Int] numList,它的reduce定义如下:

def reduce[A1 >: Int](op: (A1, A1) => A1): A1

1)[A1 >: Int]表示A1必须是Int的父类或者是本身,通常取A1Int自身,>:称为下边界,即A1的下边界为Int

2)Reduce的输入参数是一个函数,名字为opop2个输入参数,第一个参数代表当前reduce的结果,第二个参数表示当前的numList中的元素,第一个参数和第二个参数相互操作,得到一个结果,此结果将作为下一轮reduce的第一个参数,如此循环,直至最后一个numList元素;

op的第一个参数、第二个参数以及返回值类型都是A1

下面的例子代码,演示了如何对numList求和。

scala> val numList = Array(1, 3, 5, 7, 9)

scala> numList.reduce((cur,next)=>{println("cur " + cur);println("next " + next);cur+next})

输出如下。

cur 1

next 3

cur 4

next 5

cur 9

next 7

cur 16

next 9

res11: Int = 25

从输出结果看:

第一次reducecur1,即numList的第一个元素,next为当前numList的元素,即第二个元素,值为3,求和,结果为4

第二次reducecur为上一次reudce的结果4next为当前numList的元素,即第三个元素,值为54+5求和,结果为9

第三次reducecur为上一次reudce的结果9next为当前numList的元素,即第四个元素,值为79+7求和,结果为16

第四次reducecur为上一次reudce的结果16next为当前numList的元素,即第五个元素,值为916+9求和,结果为25

因为已经是numList的最后一个元素,返回最终值25,

10. reverse操作

reverse可以得到一个逆序排列的Array

例子代码如下,声明一个Int类型的数组。

scala> val numList = Array(1,3,5,7,9)

numList: Array[Int] = Array(1, 3, 5, 7, 9)

使用reverse,得到反转的数组。

scala> numList.reverse

res0: Array[Int] = Array(9, 7, 5, 3, 1)

11. 排序操作

1)普通排序

sorted可以得到一个按序排列的Array

例子代码如下,先声明Int类型的数组。

scala> val numList = Array(1,3,2,0,1)

numList: Array[Int] = Array(1, 3, 2, 0, 1)

使用sorted可以得到一个按升序排列的数组。

scala> numList.sorted

res1: Array[Int] = Array(0, 1, 1, 2, 3)

2)快速排序

使用quickSort,可以对数组进行快速排序,排序后,原数组中的数据将按序排列。

声明Int数组numList

scala> val numList = Array(1,3,2,0,1)

numList: Array[Int] = Array(1, 3, 2, 0, 1)

快速排序

scala> scala.util.Sorting.quickSort(numList)

验证,numList中的元素已经按序排列。

scala> numList

res9: Array[Int] = Array(0, 1, 1, 2, 3)

3)按照所选择的值进行排序

使用sortBy,可以对数组中的每个元素进行处理,然后对处理后的元素进行排序。

例如,声明Int数组numList

scala> val numList = Array(1,3,2,0,1)

numList: Array[Int] = Array(1, 3, 2, 0, 1)

numList中每个元素对3取余,然后进行排序,使用sortBy

sortBy的定义如下,输入参数是ff是一个函数,f的参数是Int类型,返回值是B

def sortBy[B](f: Int => B)(implicit ord: scala.math.Ordering[B]): Array[Int]

例子代码如下,f的输入参数是nnnumList的一个元素,f实现了n3取余,返回值就是n3的余数,以此余数进行排序,结果如下。

scala> numList.sortBy(n=>n%3)

res3: Array[Int] = Array(3, 0, 1, 1, 2)

4)自定义排序

使用sortWith可以自定义排序,例如按升序、降序排列,例子代码如下。

声明Int数组numList

scala> val numList = Array(1,3,2,0,1)

numList: Array[Int] = Array(1, 3, 2, 0, 1)

sortWith函数定义如下。

def sortWith(lt: (Int, Int) => Boolean): Array[Int]

sortWith的参数为一个函数ltlt2个参数,第一个参数为numList的当前元素,第二个参数为numList的下一个元素,返回值是Boolean类型,即第一个参数和第二个参数的比较结果,如果为true,则说明第一个参数>第二个参数,排列顺序不变,如果为false,则说明第一个参数<第二个参数,两者要换一个顺序,至于,何为true,何为false,则由我们自己来规定(=>右边的表达式),可以是直接比大小,也可以是自定义的规则,总之,按照相同的规则,返回Boolean即可。

例子代码如下,实现降序排列,=>右边为比较规则:cur>next,即cur>next时,返回true,顺序不变,否则返回false,交换顺序。

scala> numList.sortWith((cur,next)=>cur>next)

res0: Array[Int] = Array(3, 2, 1, 1, 0)

利用sortWith传入的比较函数,可以实现自定义排序。

12. 多维数组

声明一个34列的数组,例子代码如下。

scala> val nums = Array.ofDim[Int](3,4)

nums: Array[Array[Int]] = Array(Array(0, 0, 0, 0), Array(0, 0, 0, 0), Array(0, 0, 0, 0))

多维数组的访问,设置第2行,第3列元素的值为4,第一个括号内为行号,编号从0开始,第二个括号内为列号,编号从0开始。

scala> nums(1)(2) = 4

打印数组内容,x表示nums数组的一行,可以看到第2行,第3列元素的值为4,因为index编号是从0开始的,因此,使用index时,要减一。

scala> nums.foreach(x=>{x.foreach(print);println})

0000

0040

0000

上面的例子,也可以使用val nums = new Array[Array[Int]](3)来声明,但是nums(0)nums(1)等,每个元素只是一个指向Array[Int]的引用,因此,还需要重新new,这种方式麻烦,容易出错,好处是,每行的大小可以不固定。

 

加艾叔微信,加入Linux(Shell+Zabbix)、大数据(Spark+Hadoop)、云原生(Docker+Kubernetes)技术交流群

 

 

关注艾叔公众号,获取更多一手信息

 

 

收藏 推荐 打印 | 阅读:
相关新闻