快学Scala(第二版)-03-数组相关操作


本章要点:

  • 若长度固定则使用Array,若长度有可能有变化则使用ArrayBuffer
  • 提供初始值时不要使用new
  • 用()来访问元素
  • 用for(elem<- arr)来遍历元素
  • 用for(elem<- arr if …) yeild …来将原数组转型为新数组
  • Scala数组和Java数组可以互相操作,ArrayBuffer,使用scala.collection.JavaConversion中的转换函数。

3.1 定长数组

如果你需要一个长度不变的数组,可以使用Scala中的Array。例如:

scala> val nums = new Array[Int](10)
//整数数组的初始化值均为0
nums: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

scala> val s=new Array[String](3)
//字符串数组的初始化值均为null
s: Array[String] = Array(null, null, null)

//当你提供初始值时,就不用new和数组长度了
scala> val hi=Array("hello","liusong")
hi: Array[String] = Array(hello, liusong)

scala> hi.length
res2: Int = 2

scala> hi(1)
res3: String = liusong

scala> hi(0)="good morning"

scala> hi
//注意这里val定义的数组是可以修改的
res7: Array[String] = Array(good morning, liusong)

在JVM中,Scala的Array以Java数组方式实现。

3.2 变长数组

对于那种长度需要变化的数组,Scala中的数据结构为import scala.collection.mutable.ArrayBuffer

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> val b = ArrayBuffer[Int]()
//或者用new ArrayBuffer[Int]
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> b+=1
//用+=在尾端添加元素
res8: b.type = ArrayBuffer(1)

scala> b+=(1,2,3,5)
//在尾端添加多个元素,用()括起来
res9: b.type = ArrayBuffer(1, 1, 2, 3, 5)

scala> val a =Array(7,8)
a: Array[Int] = Array(7, 8)

scala> b++=a
//用++=在尾端添加一个数组
res10: b.type = ArrayBuffer(1, 1, 2, 3, 5, 7, 8)

scala> b.trimEnd(4)
//移除最后4个元素
scala> b
res12: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 2)

你也可以在任意位置插入或移除元素,但这样的操作不太高效(所有在那个位置之后的元素都必须平移)。

scala> b.insert(2,6)  //在下标2之前插入

scala> b
res14: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 6, 2)

scala> b.insert(2,3,4,5)  //你可以插入任意多个元素

scala> b
res16: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 3, 4, 5, 6, 2)

scala> b.remove(2)   //移除指定下标的元素
res17: Int = 3

scala> b
res18: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 4, 5, 6, 2)

scala> b.remove(2,3)   //第二个参数的含义是你要移除多少个元素

scala> b
res20: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 2)

有时你需要构建一个Array但不知道最终需要装多少个元素。在这种情况下,先构建一个ArrayBuffer,再调用toArray;反之,调用toBuffer可以将一个数组转换成数组缓冲。

scala> b.toArray    //
res25: Array[Int] = Array(1, 1, 2)

scala> a
res21: Array[Int] = Array(7, 8)

scala> a.toBuffer
res22: scala.collection.mutable.Buffer[Int] = ArrayBuffer(7, 8)

3.3 遍历数组和数组缓冲

一下是for循环遍历数组或数组缓冲的语法:

scala> for (i<-0 until b.length) println(s"$i:${b(i)}")
0:1
1:1
2:2

until方法和to方法很像,只不过它排除了最后一个元素。因此i的取值为0到b.length-1。
一般来说

for(i<- 	区间)

会让变量i遍历区间的所有值。

scala> 0 until 9
res26: scala.collection.immutable.Range = Range(0, 1, 2, 3, 4, 5, 6, 7, 8)

scala> 0 until 9 reverse   
warning: there were 1 feature warning(s); re-run with -feature for details
res27: scala.collection.immutable.Range = Range(8, 7, 6, 5, 4, 3, 2, 1, 0)

scala> 0 until 9 by 2
res28: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)

scala> 0 until 9 by -1
res29: scala.collection.immutable.Range = Range()

使用守卫reverseby -1可以从数组的尾部开始遍历

如果循环体中不需要用到数组下标,也可以像这样直接遍历数组元素:

scala> for (i<- b) print(i)
112

3.4 数组转换

从一个数组(或数组缓冲)出发,以某种方式对它进行转换是很容易的。这些转换动作不会修改原始数组,而是交出一个新的数组。

scala> val a= Array(2,3,4,5,6)
a: Array[Int] = Array(2, 3, 4, 5, 6)

scala> val result=for(i<-a) yield 2*i
result: Array[Int] = Array(4, 6, 8, 10, 12)

**for/yield循环创建里一个类型与原始集合相同的新集合。**如果从数组缓冲出发,那么你在for/yield之后得到的也是一个数组缓冲。

scala> val b = ArrayBuffer(9,8,7,6)
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(9, 8, 7, 6)

scala> for (i<-b) yield i*2
res3: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(18, 16, 14, 12)

当你遍历一个集合时,只想处理那些满足特定条件的元素。可以通过守卫:for中的if来实现。

scala> for(i<-b if i%2==0) yield i*2
res4: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(16, 12)

另一种函数式编程风格的做法如下:

scala> a
res5: Array[Int] = Array(1, 2, 3, 5)

scala> a.filter(_%2==0).map(2*_)
res7: Array[Int] = Array(4)
//甚至可以这样做
scala> a filter {_%2==0} map{2*_}
res8: Array[Int] = Array(4)

假定我们想从一个整数数组缓冲中移除所有的负数,传统的顺序执行方案可能是遍历数组缓冲,并在遇到不需要的元素时将它们移除。

var n=b.length
var i=0
while(i<n){
	if (a(i)>=0) i+=1
	else {a.remove(i); n-=1}
}

这有点小题大做了——你需要记住,在移除元素时不递增i,而是对n做递减操作。从数组缓冲中移除元素也并不高效。这个循环不必要地移动了后面会被移除的元素

Scala中明显的解决方案是使用for/yield循环并保持所有的非负数元素:

scala> val b= ArrayBuffer(-1,8,3,-4,90)
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(-1, 8, 3, -4, 90)

scala> val result =for (i<-b if i>=0) yield i
result: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 3, 90)

结果是一个新的数组缓冲。
假定我们需要修改原始数组缓冲,我们可以收集这些元素的位置,使用indices方法;然后从后往前移除这些位置的元素:

scala> val positionToRemove=for(i<-b.indices if b(i)<0) yield i
positionToRemove: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 3)

scala> for (i<-positionToRemove.reverse) b.remove(i)

scala> b
res2: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 3, 90)

或者更好的办法,记住要保留的位置,将对应的元素(往前)复制,最后缩短数组缓冲

scala> val b= ArrayBuffer(-1,8,3,-4,90)
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(-1, 8, 3, -4, 90)

scala> val positionToKeep=for (i<-b.indices if b(i)>=0) yield i
positionToKeep: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 4)

scala> for (j<-positionToKeep.indices) b(j)=b(positionToKeep(j))

scala> b.trimEnd(b.length-positionToKeep.length)

scala> b
res5: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 3, 90)

3.5 常用算法

也许你听过过,很大的比例的业务运算不过实在求和与排序

  • sum,数组的元素类型必须要是数值类型
scala> Array(1.7,Pi,34).sum
res15: Double = 38.8415926535897
  • minmax,交出数组或数组缓冲中最大或最小的元素
scala> ArrayBuffer("Mary","Lucy","Chris").max
res16: String = Mary

scala> ArrayBuffer("Mary","Lucy","Chris","little").max
res17: String = little

这里值得推敲,对于一个字符串数组缓冲,是根据ASCII值比较出最大值

  • sorted方法,返回经过排序的数组或数组缓冲,不修改原始数组
scala> val bSorted=b.sorted
bSorted: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 8, 90)

scala> b
res19: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(8, 3, 90)
  • sortWith方法,你还可以提供一个比较函数,比如降序排列
scala> val bDescending=b.sortWith(_>_)
bDescending: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(90, 8, 3)
  • quickSort函数,直接对数组进行排序

你可以直接对一个Array排序,但是不能直接对一个ArrayBuffer排序


scala> val a=Array(3,45,1,30)
a: Array[Int] = Array(3, 45, 1, 30)

scala> import scala.util.Sorting.quickSort
import scala.util.Sorting.quickSort

scala> quickSort(a)

scala> a
res22: Array[Int] = Array(1, 3, 30, 45)

scala> quickSort(b)
<console>:14: error: overloaded method value quickSort with alternatives:
  (a: Array[Float])Unit <and>
  (a: Array[Int])Unit <and>
  [K](a: Array[K])(implicit evidence$1: scala.math.Ordering[K])Unit <and>
  (a: Array[Double])Unit
 cannot be applied to (scala.collection.mutable.ArrayBuffer[Int])
              quickSort(b)
              ^
  • mkString,将数组或数组缓冲转换成字符串,并可指定元素之间的分隔符
scala> a.mkString(" and ")
res26: String = 1 and 3 and 30 and 45

该方法的另一个重载版本,可以让你指定前缀和后缀

scala> val c=Array(1,2,3,45,5)
c: Array[Int] = Array(1, 2, 3, 45, 5)

scala> c.mkString("<",",",">")
res37: String = <1,2,3,45,5>

注意:数组的toString方法是调用的Java的毫无意义的toString方法,而数组缓冲可以查看类型和元素:

scala> c.toString
res38: String = [I@27ada0e8

scala> b.toString
res39: String = ArrayBuffer(8, 3, 90)

3.6 解读Scaladoc

数组和数组缓冲有许多有用的方法,可以浏览Scala文档来获取这些信息。

  • a.count(_>0) 清点有多少个大于0的元素
  • b.append(1,2,3) 对b追加元素
  • ++
scala> var d=b++"cd"
d: scala.collection.mutable.ArrayBuffer[AnyVal] = ArrayBuffer(8, 3, 90, c, d)

3.7 多维数组

和Java一样,Scala的多维数组是通过数组的数组来实现的。

要构造一个,Double的二维数组类型为Array[Array[Double]],可以用ofDim方法:

scala> val matrix=Array.ofDim[Double](3,4)
matrix: Array[Array[Double]] = Array(Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 0.0, 0.0))

scala> matrix(2)(3)=23  //用两对括号访问数组的元素

scala> matrix
res47: Array[Array[Double]] = Array(Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 0.0, 23.0))

注意:这里有一个易踩的坑,多维数组访问,索引是从编号0开始

你可以创建不规则数组,每一行长度各不相同:

scala> val triangle=new Array[Array[Int]](4)
triangle: Array[Array[Int]] = Array(null, null, null, null)

scala> for (i<-triangle.indices) triangle(i)=new Array[Int](i+1)

scala> triangle
res52: Array[Array[Int]] = Array(Array(0), Array(0, 0), Array(0, 0, 0), Array(0, 0, 0, 0))

练习

  • 编写一段代码,将a设置为一个n个随机整数的数组,要求随机数介于0和n之间
scala> 
def mkarray(n:Int)={
   val a =new Array[Int](n)
   for (i<- 0 until n) a(i)=new scala.util.Random().nextInt(n)
   a
}
mkarray: (n: Int)Array[Int]

scala> mkarray(9)
res61: Array[Int] = Array(7, 6, 5, 0, 4, 4, 2, 8, 8)

scala> mkarray(9)
res62: Array[Int] = Array(8, 5, 0, 7, 4, 6, 0, 1, 1)

scala> mkarray(9)
res63: Array[Int] = Array(7, 8, 5, 5, 3, 5, 1, 8, 1)
  • 编写一个循环,将整数数组中相邻的元素置换。
scala> def substitution(a:Array[Int])={
     | for (i<- 0 until (a.length-1) by 2) { val t=a(i); a(i)=a(i+1);a(i+1)=t}
     | a
     | }

scala> a
res94: Array[Int] = Array(2, 3, 3, 7, 0, 1, 0, 5, 5)

scala> substitution(a)
res95: Array[Int] = Array(3, 2, 7, 3, 1, 0, 5, 0, 5)
  • ~~重复前一个练习,不过这一次生成一个新的值交换过的数组。使用for/yield。~
object App
{
  def main(args: Array[String]) {
    val a = Array(1, 2, 3, 4, 5);
    val b = revertYield(a);
    b.foreach(println);
  }
  def revertYield(arr : Array[Int]) = {
    for (i <- 0 until arr.length) yield {
      if (i < (arr.length - 1) && i % 2 == 0) {
        val t = arr(i);
        arr(i) = arr(i + 1);
        arr(i + 1) = t;
      }
      arr(i);
    }
  }
}
  • 给定一个整数数组,产出一个新的数组,包含数组中的所有正值,以原有数组顺序排列;之后的元素是所有零或负值,以原有顺序排列。
scala> def pone(a:Array[Int])={
     | var pone= ArrayBuffer[Int]()
     | pone ++=( for(i<-a if i>0) yield i)
     | pone ++=( for(i<-a if i<=0) yield i)
     | pone}
pone: (a: Array[Int])scala.collection.mutable.ArrayBuffer[Int]

scala> b
res4: Array[Int] = Array(-959, 491, 308, 895, 359, 1325, -511)

scala> pone(b)
res5: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(491, 308, 895, 359, 1325, -959, -511)

  • 如何计算Array[Double]的平均值
scala> def mkarray(n:Int)={
    val a =new Array[Double](n)
    for (i<- 0 until n) a(i)=new scala.util.Random().nextDouble()*10
    a
    }
mkarray: (n: Int)Array[Double]

scala> val b=mkarray(6)
b: Array[Double] = Array(5.209708976240694, 6.883409231622068, 3.6504482299879424, 6.975258597309519, 8.3932468226679, 8.49206155876563)

scala> mean(b)

scala> def mean(a:Array[Double]) ={
	a.sum/a.length
}
mean: (a: Array[Double])Double

scala> mean(b)
res130: Double = 6.600688902765626
  • 如何重新组织Array[Int] 的元素,将它们反序排列
def arrReverse(a:Array[Int])={
    for (i<-0 until a.length/2) {
        val t=a(i)
        a(i)=a(a.length-i-1)
        a(a.length-i-1)=t
    }
    a
}

scala> arrReverse(b)
res15: Array[Int] = Array(-511, 1325, 359, 895, 308, 491, -959)

scala> arrReverse(b)
res15: Array[Int] = Array(-511, 1325, 359, 895, 308, 491, -959)

  • 编写一段代码,产出数组中的所有值,去掉重复项
scala> val a=mkarray(9)
a: Array[Int] = Array(2, 8, 3, 2, 2, 8, 6, 8, 5)

scala> a.distinct
res0: Array[Int] = Array(2, 8, 3, 6, 5)
  • 去掉数组缓冲中,去除负数元素,除第一个之外

scala> def delunfirstF(a:Array[Int])={
     |     val n=for (i<-a.indices if a(i)<0) yield i
     |     val remv= n.reverse.dropRight(1)
     |     val temp=a.toBuffer
     |     for(index<-remv) temp.remove(index)
     |     temp
     | }
delunfirstF: (a: Array[Int])scala.collection.mutable.Buffer[Int]

scala> a
res17: Array[Int] = Array(2, 8, -10, 2, 2, 8, -9, 8, 5)

scala> delunfirstF(a)
res18: scala.collection.mutable.Buffer[Int] = ArrayBuffer(2, 8, -10, 2, 2, 8, 8, 5)

  • 创建一个由java.util.TimeZone.getAvailableIDs返回的时区集合,判断条件是它们在美洲。去掉"America/"前缀并反序。
scala> var timezones=ArrayBuffer[String]()
timezones: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()

scala> timezones ++=java.util.TimeZone.getAvailableIDs()
res22: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(Africa/Abidjan, Africa/Accra, Africa/Addis_Ababa, Africa/Algiers, Africa/Asmara, Africa/Asmera, Africa/Bamako, Africa/Bangui, Africa/Banjul, Africa/Bissau, Africa/Blantyre, Africa/Brazzaville, Africa/Bujumbura, Africa/Cairo, Africa/Casablanca, Africa/Ceuta, Africa/Conakry, Africa/Dakar, Africa/Dar_es_Salaam, Africa/Djibouti, Africa/Douala, Africa/El_Aaiun, Africa/Freetown, Africa/Gaborone, Africa/Harare, Africa/Johannesburg, Africa/Juba, Africa/Kampala, Africa/Khartoum, Africa/Kigali, Africa/Kinshasa, Africa/Lagos, Africa/Libreville, Africa/Lome, Africa/Luanda, Africa/Lubumbashi, Africa/Lusaka, Africa/Malabo, Africa/Maputo, Africa/Maseru, Africa/Mbabane, Africa/Mogadishu, Africa/Monrovia, Africa/Nairobi, Africa/Ndjamena, ...

scala> timezones=timezones.filter(_.take(8)=="America/")
mutated timezones

scala> timezones
res23: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(America/Adak, America/Anchorage, America/Anguilla, America/Antigua, America/Araguaina, America/Argentina/Buenos_Aires, America/Argentina/Catamarca, America/Argentina/ComodRivadavia, America/Argentina/Cordoba, America/Argentina/Jujuy, America/Argentina/La_Rioja, America/Argentina/Mendoza, America/Argentina/Rio_Gallegos, America/Argentina/Salta, America/Argentina/San_Juan, America/Argentina/San_Luis, America/Argentina/Tucuman, America/Argentina/Ushuaia, America/Aruba, America/Asuncion, America/Atikokan, America/Atka, America/Bahia, America/Bahia_Banderas, America/Barbados, America/Belem, America/Belize, America/Blanc-Sablon, America/Boa_Vista, America/Bogota, America/Boise, America/Buenos_Aires, America/Cambridge_Bay, Ameri...

scala> timezones=timezones.map(_.drop(8)).sorted
mutated timezones

scala> timezones
res24: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(Adak, Anchorage, Anguilla, Antigua, Araguaina, Argentina/Buenos_Aires, Argentina/Catamarca, Argentina/ComodRivadavia, Argentina/Cordoba, Argentina/Jujuy, Argentina/La_Rioja, Argentina/Mendoza, Argentina/Rio_Gallegos, Argentina/Salta, Argentina/San_Juan, Argentina/San_Luis, Argentina/Tucuman, Argentina/Ushuaia, Aruba, Asuncion, Atikokan, Atka, Bahia, Bahia_Banderas, Barbados, Belem, Belize, Blanc-Sablon, Boa_Vista, Bogota, Boise, Buenos_Aires, Cambridge_Bay, Campo_Grande, Cancun, Caracas, Catamarca, Cayenne, Cayman, Chicago, Chihuahua, Coral_Harbour, Cordoba, Costa_Rica, Creston, Cuiaba, Curacao, Danmarkshavn, Dawson, Dawson_Creek, Denver, Detroit, Dominica, Edmonton, Eirunepe, El_Salvador, Ensenada, Fort_Nelson, Fort_Wayn...
  • 引入java.awt.datatransfer.并构建一个类型为SystemFlavorMap类型的对象:
    val flavors = SystemFlavorMap.getDefaultFlavorMap().asInstanceOf[SystemFlavorMap] 然后以DataFlavor.imageFlavor为参数调用getNativesForFlavor方法,以Scala缓冲保存返回值。 (为什么用这样一个晦涩难懂的类?因为在Java标准库中很难找到使用java.util.List的代码)
scala> val flavors = SystemFlavorMap.getDefaultFlavorMap().asInstanceOf[SystemFlavorMap]
flavors: java.awt.datatransfer.SystemFlavorMap = java.awt.datatransfer.SystemFlavorMap@1e1f808c

scala> println(flavors.getNativesForFlavor(DataFlavor.imageFlavor).toArray.toBuffer.mkString(" | "))
image/png | image/x-png | image/jpeg | image/gif | PNG | JFIF
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值