5.数组

几乎所有语言都会提供数组这种数据结构。数组这种数据结构具有取值快、更新快、追加快的优点。当然也有缺点

,比如说插入慢、删除慢等(插入删除针对ArrayBuffer)。本节我们就来看下Scala中的数组

1.定长数组

Scala中使用Array来表示长度不变的数组,Scala中的Array是易变的(即数组元素支持更新操作)

我们创建一个数组,可以使用Array类的伴生对象的apply方法进行创建。由于在scala中直接输入伴生对象()就会调用

伴生对象的apply方法。所以我们可以书写如下代码:

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

上述代码生成了一个存取Int类型数值的数组,该数组的长度的4。

我们要根据数组的index得到数组的值,可以使用:

val first = numbers(0)

数组下标从0开始.numbers(i)这种写法其实是调用了Array类的apply方法。

由于Scala中的Array是易变的,那么我们应该如何更新数组的元素呢?请看如下代码:

numbers(3) = 100

 

上述代码把数组中第四个元素的值更新为100。numbers(i)=num这种语法实际上是调用了Array类的update方法。

Scala中还通过隐式转换调用ArrayOps类为Array类增加了不少可以间接调用的方法。

 

除了使用伴生对象来实例化Array类,我们还可以直接new Array类的实例,实例化Array类的实例的时候要制定类型参数,如下:

val players = new Array[String](5)
players(0)="James"
players(1)="Kobe"
players(2)="Curry"
players(3)="Westbrook"
players(4)="Bosh"

 

完整的代码如下:

技术分享

2.变长数组

在Java中ArrayList是变长的数组,当数组容量用完的时候,ArrayList会去申请额外的空间,并把原来的数组元素复制过去。

在Scala中变长数组的实现类为ArrayBuffer。

Scala中的类一般都有非常多的方法,我们不可能再次一一列举。感兴趣的读者应该去通读一遍scala doc。

直接上代码:

package chapter03

import scala.collection.mutable.ArrayBuffer

object TestArrayBuffer extends App {
  //ArrayBuffer可以通过ArrayBuffer的伴生对象来实例化,可以直接new ArrayBuffer类的实例
  //通过ArrayBuffer的伴生对象实例化一个ArrayBuffer的实例。
  val players = ArrayBuffer("James")
  //取出可变数组的第一个元素,会调用ArrayBuffer类的apply方法
  println(players(0))
  //往尾部添加一个元素
  players += "Kobe"
  //往尾部添加多个元素,括号中实际上是可变参数
  players += ("Curry","Westbrook")
  //往尾部添加一个Array
  players ++= Array("Bosh","Duncan")
  //打印数组元素
  for(player <- players) print(player+" ")
  //从数组尾部删除元素是个高效的操作,因为不会导致其他元素的移动,尾部删除使用如下方法,参数是从尾部删除的元素的个数
  players trimEnd 1
  //从可变数组中间删除或者插入元素是低效的。因为会导致其他元素的移动。
  //我们可以使用insert在可变数组的任意位置插入元素
  players.insert(2, "Parker")
  //insert还有另外一个重写的方法,接受可变参数,可以传入多个元素,如下:
  players.insert(1,"Leonard","Ginobili")
  //删除元素,指定数组下标即可
  players.remove(players.length-1)
  //删除元素,有一个重载的方法,能够指定要删除的元素的个数
  players.remove(0,2)
  //由于ArrayBuffer是可变的,跟函数式编程的理念有点背道而驰。所以做完所有操作,最好把它转成Array在使用
  players.toArray
}

 

 3.数组的遍历

Scala中数组的遍历跟Java中不太一样。scala遍历数组跟其他函数式语言遍历数组的方式基本一致。看下代码即可:

package chapter03

object 遍历数组 extends App {
  //定义一个不变数组
  val words = Array("a","b","c","d")
  //如果遍历数组的时候,需要使用到数组小标的话,我们可以通过如下方式遍历数组
  for(i <- 0 until words.length)  println(i+" : "+ words(i))
    
  //如果要两个元素一跳,可以这样遍历:
  for(i <- 0 until (words.length,2)) println(i+" : "+ words(i))
    
  //如果要从数组尾部开始遍历数组的话,可以这样遍历:
  for(i <- (0 until words.length).reverse) println(i+" : "+ words(i))
    
  //如果遍历过程中使用不到数组下标,可以使用入下语法进行遍历
  for(word <- words) println(word)
    
}

 数组的其他一些易用点:

package test
import scala.collection.mutable.ArrayBuffer

object 数组测试 extends App {

  val arr = Array(1,2,3,4)
  val arrBuff = ArrayBuffer(1,2,3,4)
  //通过打印发现Array没有实现toString
  println(arr)
  //通过打印发现ArrayBuffer实现了toString
  println(arrBuff)
  //返回数组的和,数组中的对象必须是数值类型
  println(arrBuff.sum)
  //返回数组的最大值
  println(arrBuff.max)
}

 

4.生成新的数组 

Scala的Array上定义了一组方法。在Array调用这些方法不会修改原始数组,而是会生成一个新的数组。看代码:

package chapter03

object 生成新的数组 extends App {

  //可以利用for推导式生成新的数组
  val a = Array(1,2,3,4,5,6,7)
  val result = for(elem <- a) yield 2*elem
  for(r <- result) print(r+" ")
  println()
  
  //可以在for推导式中利用守卫进行过滤操作,如下:找出数组中的偶数
  val result2 = for(elem <- a if elem%2==0) yield elem
  for(r <- result2) print(r+" ")
  println()
  
  //可以是使用filter函数,如下
  val result3 = a.filter((x:Int)=>x%2==0)
  for(r <- result3) print(r+" ")
}

5.数组元素排序

Scala中的数组排序使用到了Ordering特质。Array类的排序方法sorted会隐式转换为对ArrayOps的调用。

为了降低问题的复杂度,我们以ArrayBuffer的排序为例来讲解。

在Scala中对象排序一般借助于Ordering特质。Ordering特质中有一个抽象方法compare。如下:

技术分享

所以在数组元素比较大小的时候,我们只需给sorted方法传入Ordering的实现类即可。下看一个简单的例子:

package test
import scala.collection.mutable.ArrayBuffer

object 数组排序 extends App {

  val numbers = ArrayBuffer(8,2,4,1,9,5)
  
  val sorted = numbers.sorted
  //使用mkString方法,指定分割字符,可以快速打印出数组。
  println(sorted mkString " " )
  
}

 

上述完成了对Array[Int]数组的排序。细心的读者可能已经发现我们并没有给sorted方法传入Ordering的实现类啊

。要解释这个问题,我们需要看一下sorted类的签名:

技术分享

从上述方法中可以看出sorted类接受一个Ordering类参数,而且该参数是个隐式参数。而Ordering类的伴生对象中还定义了很多

隐式对象,如下:

技术分享

其实在Scala中所有的继承自AnyVal的类T都不需要手动实现Ordering[T],因为Scala已经帮你实现好了。

比如Ordering[Int]的实现类实际上是BigInt(不是scala.math包中的BigInt)。如下:

技术分享

正是因为上述的原因,我们才不需要为继承自AnyVal的类自行实现Ordering。

甚至Ordering[String]也不用手动实现(Ordering的伴生对象已经帮我们实现了)

package test
import scala.collection.mutable.ArrayBuffer

object 简单数组排序 extends App {

  val numbers = ArrayBuffer(8,2,4,1,9,5)
  //Ordering的伴生对象已经帮我们实现了Ordering[Int]了,直接使用即可
  val sorted = numbers.sorted
  //使用mkString方法,指定分割字符,可以快速打印出数组。
  println(sorted mkString " " )
  
  //排序的时候还可以指定排序算法,这次倒序排列
  val sorted2 = numbers.sorted(new scala.math.Ordering[Int](){
    def compare(x:Int, y:Int):Int=y-x
  })
  println(sorted2 mkString " " )
  
  //Ordering的伴生对象已经帮我们实现了Ordering[String]了,直接使用即可应用
  val players = ArrayBuffer("g","f","t","a")
  val sorted3 = players.sorted
  println(sorted3 mkString " " )
  
}

 

下面看几个复杂的数组排序的例子,如下:

package test

object 复杂数组排序 extends App {
  
  //定义一个复杂的数组
  val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3))
  //我们按照第二个字段排序,需要自定义Ordering
  val sorted = pairs.sorted(new scala.math.Ordering[(String,Int,Int)](){
    def compare(x:(String,Int,Int),y:(String,Int,Int)):Int={
      x._2 - y._2
    }
  }) 
  println(sorted mkString " ")
  
  //下面演示,如何先按照第三个字段排序,在按照第一个排序,使用自定义Ordering
  val pairs2 = Array(("a", 5, 3), ("c", 3, 1), ("b", 1, 3))
  //自定义实现Ordering的compare方法
  val sorted2 = pairs2.sorted(new scala.math.Ordering[(String,Int,Int)](){
    def compare(x:(String,Int,Int),y:(String,Int,Int)):Int={
      if(x._3>y._3) 1 else if(x._3<y._3) -1 else x._1.compareTo(y._1)
    }
  })
  println(sorted2 mkString " ")
  
  //其实scala中Ordering自带了两个方法,简化了上述的代码,如下:
  //我们按照第二个字段排序
  val sorted3 = pairs.sorted(Ordering.by[(String,Int,Int),Int]((x:(String,Int,Int))=>x._2) )
  //下面演示,如何先按照第三个字段排序,在按照第一个排序
  val sorted4 = pairs2.sorted(Ordering[(Int, String)].on[(String,Int,Int)]((x:(String,Int,Int)) => (x._3, x._1)))
  println(sorted4 mkString " ")
}

 6.二维数组

二维数组不想多说,看如下例子即可:

package test

object 多维数组 extends App {
  
  //用Array的伴生对象定义数组,第二维的长度都不相同
  val matrix = Array[Array[Int]](Array(1),Array(1,2),Array(1,2,3))
  for(m <- matrix;n <- m) print(n+" ");println()
  println(matrix(0).length)
  println(matrix(1).length)
  println(matrix(2).length)
  //使用ofDim来创建各维长度都相同的二维数组
  val matrix2 = Array.ofDim[Int](3, 3);
  matrix2(1)(1)=1
  println(matrix2(0).length)
  println(matrix2(1).length)
  println(matrix2(2).length)
}

 

7.scala数组和Java的互操作

Scala中的数组可以利用Scala的隐式转换和Java的List进行互相转换。直接上代码,用到时回来查

package test
//把scala中的buffer转换为Java中的List
import scala.collection.JavaConversions.bufferAsJavaList
//把Java中的List转换为scala中的buffer
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Buffer

object 与Java互操作 extends App {

  val commond = ArrayBuffer("ls"," -al","/home")
  //把scala中的buffer转换为Java中的List
  val pb = new ProcessBuilder(commond)
  pb.start()
  //把Java中的List转换为scala中的buffer
  val cmd:Buffer[String] = pb.command()
  
}

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。