Scala學習(三)----數組相關操作


數組相關操作

摘要:

本篇主要學習如何在Scala中操作數組。JavaC++程序員通常會選用數組近似的結構(比如數組列表向量)來收集一組元素。在Scala中,我們的選擇更多,不過現在我們先假定不關心其他選擇,而只是想馬上開始用數組。本篇的要點包括:

1. 若長度固定則使用Array,若長度可能有變化則使用ArrayBuffer

2. 提供初始值時不要使用new

3. 用()來訪問元素

4. 用for (elem<-arr)來遍歷元素

5. 用for (elem<-arr if…)…yield…來將原數組轉型為新數組

6. Scala數組和java數組可以互操作;用AnayBuffer,使用scalacollection.JavaConversions中的轉換函數

定長數組

如果你需要一個長度不變的數組,可以用Scala中的Array。例如:

val nums=new Array[Int] (10) //長度為10的整數數組,所有元素初始化為0

val a=new Array [String] (10) //長度為10的字符串數組,所有元素初始化為null

val s= Array("Hello", "World") //長度為2的Array[String]類型是推斷出來的,已提供初始值就不需要new

S (0) ="Goodbye" //Array("Goodby ","World"),使用()而不是[]來訪問元素

在JVM中,Scala的Array以Java數組方式實現。示例中的數組在JVM中的類型為java.lang.String[]。Int、Double或其他與Java中基本類型對應的數組都是基本類型數組。

舉例來說,Array(2,3,5,7,11)在JVM中就是一個int[]

變長數組:緩沖

尾端操作緩沖數組

對於那種長度按需要變化的數組Java有ArrayList,C++有vector。Scala中的等效數據結構為ArrayBuffer

import scala.collection.mutable.ArrayBuffer

val b=ArrayBuffer[lnt]() // 或者new ArrayBuffer [int],一個空的數組緩沖,准備存放整數

b+=1 // ArrayBuffer (1),用+=尾端添加元素

b+=(1,2,3,5) // ArrayBuffer(1,1,2,3,5),在尾端添加多個元素,以括號包起來

b++= Array(8, 13, 21) // ArrayBuffer(1, 1, 2, 3, 5, 8,13, 21) //用++=操作符追加任何集合

b.trimEnd(5) // ArrayBuffer(1, 1, 2),移除最后5個元素

在數組緩沖的尾端添加或移除元素是一個高效的操作

任意位置操作緩沖數組

你也可以在任意位置插入或移除元素,但這樣的操作並不那么高效。所有在那個位置之后的元素,都必須被平移。舉例如下:

b.insert (2,6) //ArrayBuffer(1, 1, 6, 2),在下標2之前插入

b.insert (2,7,8,9) // ArrayBuffer(1, 1,7,8,9, 6,2),你可以插入任意多的元素

b.remove(2) // ArrayBuffer(1,1,8,9,6,2)

b.remove (2,3) //ArrayBuffer(1,1, 2),第2個參數的含義是要移除多少個元素

有時你需要構建一個Array,但不知道最終需要裝多少元素。在這種情況下,先構建一個數組緩沖,然后調用:

b.toArray //Array(1, 1,2)

反過來,調用亂toBuffer可以將一個數組a轉換成一個數組緩沖

遍歷數組和數組緩沖

全遍歷

在Java和C++中,數組數組列表向量有一些語法上不同,Scala則更加統一。大多數時候,你可以用相同的代碼處理這兩種數據結構。以下是for循環遍歷數組或數組緩沖的語法:

for (i <- 0 until a.length) //變量i的取值從0到a length -1

println(i+":"+a(i))

utiI是Richlnt類的方法,返回所有小於但不包括上限的數字。例如:

0 until 10 // Range(0,1,2,3,4,5,6,7,8, 9)

需要注意的是,0 until 10實際上是一個方法調用:0.until(10)

條件遍歷

如下結構:

for(I <- 區間)

會讓變量i遍歷該區間的所有值。拿本例來說,循環變量i先后取值0、1,等等,直到但不包含a.length。如果想要每兩個元素一跳,可以讓i這樣來進行遍歷:

0 until (a.length,2) //Range(0,2,4,…)

如果要從數組的尾端開始,遍歷的寫法為:

(0 until a.length) .reverse //Range(...,2,1,0)

如果在循環體中不需要用到數組下標,我們也可以直接訪問數組元素,就像這樣:

for (elem <- a)

println (elem)

這和Java中的"增強版"for循環,或者C++中的"基於區間的"for循環很相似。變量elem先后被設為a(0),然后a(1),依此類推

數組轉換

for中的推導式和守衛

在前面,你看到了如何像Java或C++那樣操作數組。不過在Scala中,你可以走得更遠。從一個數組或數組緩沖出發,以某種方式對它進行轉換是很簡單的。這些轉換動作不會修改原始數組,而是產生一個全新的數組。像這樣使用for推導式

val a=Array(2, 3, 5, 7, 11)

val result=for (elem <- a) yield 2*elem //result是Array(4,6,10, 14, 22)

for(…)yield循環創建了一個類型與原始集合相同的新集合。如果你從數組出發,那么你得到的是另一個數組。如果你從數組緩沖出發,那么你在for(…)yield之后得到的也

是數組緩沖

結果包含yield之后的表達式的值,每次迭代對應一個。通常,當你遍歷一個集合時,你只想處理那些滿足特定條件的元素。這個需求可以通過守衛for中的if來實現。在這里我們對每個偶數元素翻倍,並丟掉奇數元素:

for (elem <- a if elem%==0) yield 2*elem

請留意結果是個新的集合,原始集合並沒有受到影響

一種等價方法

除上述之外,還有另一種做法是

a.filter (_%2==0).map(2*_)

甚至

a.filter { _%2 == 0 } map {2*_ }

某些有着函數式編程經驗的程序員傾向於使用filtermap而不是守衛yield,這不過是一種風格罷了與for循環所做的事完全相同。你可以根據喜好任意選擇。

高效數組操作

考慮如下示例:給定一個整數的數組緩沖,我們想要移除除第一個負數之外的所有負數傳統的依次執行的解決方案會在遇到第一個負數時置一個標記,然后移除后續出現的負數元素

var first=true

var n=a.length

var i=0

while ( i<n ) {

if (a(i) >= 0)

i+=1

else{

if (first) {

first=false

i+=1

} else {

a.remove (i)

n-=1

}

}

}

但這個方案其實並不那么好:從數組緩沖中移除元素並不高效,把非負數值拷貝到前端要好得多。

首先收集需要保留的下標:

var first= true

val indexes=for (i <- 0 until a.length if first || a(i)>=0) yield {

if (a(i)<0)

first=false;

i

}

然后將元素移動到該去的位置,並截斷尾端:

for(j <- 0 until indexes.length)

a(j)= a(indexes(j))

a.trimEnd (a.length -indexes.length)

這里的關鍵點是,拿到所有下標好過逐個處理

常用算法

求和與排序

有一種說法,很大比例的業務運算不過是在求和與排序。還好Scala有內建的函數來處理這些任務

Array(1,7,2, 9).sum // 19,對ArrayBuffer同樣適用

要使用sum方法,元素類型必須是數值類型:要么是整型,要么是浮點數或者Biglnteger/BigDecimal。

同理,min和max輸出數組或數組緩沖中最小和最大的元素。

ArraryBuffer("Mary", "had","a","little", "lamb").max // "little"

sorted方法將數組或數組緩沖排序並返回經過排序的數組或數組緩沖,這個過程並不會修改原始版本:

val b=ArrayBuffer(1,7,2, 9)

val bSorted=b.sorted(_ < _) // b沒有被改變,bSorted是ArrayBuffer(1,2,7,9)

還可以提供一個比較函數,不過你需要用sortWith方法:

val bDescending=b.sorted(_ > _) // ArrayBuffer(9,7,2, 1)

可以直接對一個數組排序,但不能對數組緩沖排序

val a=Array(1,7,2,9)

scala.util. Sorting.quickSortIa(a) // a現在是Array(1,2,7,9)

關於num、max和quickSort方法,元素類型必須支持比較操作,這包括了數字、字符串以及其他帶有Ordered特質的類型。

顯示數組內容

最后,如果你想要顯示數組數組緩沖的內容,可以用mkString方法,它允許你指定元素之間的分隔符。該方法的另一個重載版本可以讓你指定前綴后綴。例如:

a.mkString("and") // "1 and 2 and 7 and 9"

a.mkString("<" , "," , ">") // "<1,2,7,9>"

和toString相比:

a.toString // " [I@85b8d",這里被調用的是Java的毫無意義的toString方法

b.toString // "ArrayBuffer(l,7,2, 9)",toString方法報告了類型,便於調試

解讀Scaladoc

數組和數組緩沖有許多有用的方法,我們可以通過瀏覽Scala文檔來獲取這些信息。對Array類的操作方法列在ArrayOps相關條目下。從技術上講,在數組上應用這些操作之前,數組都會被轉換成ArrayOps對象。

由於Scala的類型系統比java更豐富,在瀏覽Scala的文檔時,你可能會遇到一些看上去很奇怪的語法。所幸,你並不需要理解類型系統的所有細節就可以完成很多有用

的工作。你可以把下表用做"解碼指環"。

多維數組

和Java樣,多維數組是通過數組的數組來實現的。舉例來說,Double的二維數組類型為:

Array[Array[Double]]

要構造這樣一個數組,可以用ofDim方法

val matrix=Array.ofDim[Double](3,4) //三行,四列要訪問其中的元素,使用兩對圓括號:

matrix (row) (column) =42

你可以創建不規則的數組每一行的長度各不相同

val triangle=new ArraylArray [Int] (10)

for (i <- 0 until triangle.length)

triangle(i)=new Array[lnt] (i+1)

與Java互操作

由於Scala數組是用java數組實現的,你可以在Java和Scala之間來回傳遞。如果你調用接受返回java.utiI.List的Java方法,則當然可以在Scala代碼中使用Java的ArrayList但那樣做沒什么意思。你完全可以引入scala.collection.JavaConversions里的隱式轉換方法。這樣你就可以在代碼中使用Scala緩沖,在調用Java方法時,這些對象會被自動包裝成Java列表

舉例來說,java.lang.ProcessBuilder類有一個以List<String>為參數的構造器。以下是在Scala中調用它的寫法:

import scala.collection.JavaConversions.bufferAsJavaList

import scala.collection.mutable.ArrayBuffer

val command = ArrayBuffer("ls", "-al", "/home/cay")

val pb = new ProcessBuilder(command) // Scala到Java的轉換

Scala緩沖被包裝成了一個實現了java.util.List接口的Java類的對象。反過來講,當Java方法返回java.util.List時,我們可以讓它自動轉換成一個Buffer

import scala.collection.JavaConversions.asScalaBuffer

import scala.collection.mutable.Buffer

val cmd: Buffer[String] = pb.command() // Java到Scala的轉換

需要注意的是,不能使用ArrayBuffer——包裝起來的對象僅能保證是個Buffer。如果Java方法返回一個包裝過的Scala緩沖,那么隱式轉換會將原始的對象解包出來。拿本例來說,cmd == command。☆☆

如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下角的【推薦】。
如果,您希望更容易地發現我的新博客,不妨點擊一下左下角的【關注我】。
如果,您對我的博客所講述的內容有興趣,請繼續關注我的后續博客,我是【Sunddenly】。

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM