《Java從入門到失業》第三章:基礎語法及基本程序結構(3.9):數組(數組基本使用、數組的循環、數組拷貝、數組排序、多維數組)

3.9數組

3.9.1數組基本使用

       數組,英文叫Array,是一種數據結構,是用來存放同一數據類型數值的集合。例如存放30個int型數值、存放100個double型數值等等。

我們知道使用一個變量,需要先聲明一個變量,例如:int a;使用數組同樣也需要先聲明一個數組變量。假設我們要聲明一個int類型的數組變量,有2種方式:

int[] a;  
int b[];

但是一般Java工作者都習慣於第一種方式,因為int[]看起來更像數據類型,後面跟一個變量名。

聲明變量,實際上是在內存中給它分配一塊空間。但是數組是存放若干個數據,因此還得繼續聲明它的大小,即存放多少個數據。Java中使用new運算符來操作。像下面這樣:

a = new int[30]; 

我們還可以在聲明數組的同時就分配空間:

int[] a = new int[30];

上面這條語句聲明int型的數組a可以存放30個int的數值。這樣,就會在內存中分配30個連續的空間。

       數組大小分配好了以後。我們要訪問數組中的某一個元素的話,可以用一個整型的下標(index)來訪問。下標是從0開始的,因此上面的數組a的下標是0~29。比如我們要訪問第29個元素,那麼可以用a[28]。這裡需要注意,數組一旦被創建了以後,大小就是固定的。如果下標超出範圍,例如訪問a[30],程序會報異常,異常一般是:「array index out of bounds」,稱做「下標越界」。

       給數組的元素賦值就很簡單了,就像給一個普通變量賦值一樣:

a[22] = 22;

我們還可以在聲明數組的時候同時賦值,有兩種形式:

int[] a = new int[] { 1, 2, 3, 4 };  
int[] b = { 1, 2, 3, 4 }; 

注意第一種形式,[]內不需要指定大小。有的時候,我們還需要知道數組的大小,可以用a.length來獲得。例如我們想遍歷打印數組的值:

for (int i = 0; i < a.length; i++) {  
    System.out.println(a[i]);  
} 

綜合上面的討論,我們可以歸納一下數組的3要素:

  1. 聲明一個數組,有2種形式,一般採用 「數據類型[] 變量名」 的形式
  2. 給數組分配大小,用new關鍵字,形式為 「變量名=new 數據類型[大小]」。一旦分配完大小,數組的大小就固定了,可以用「變量名.length」來獲取數組的大小。訪問數組的元素用「變量名[下標]」的方式。下標的範圍是0~length。如果不在這個範圍內,程序會報「下標越界」異常。
  3. 給數組的元素賦值

訪問數組的元素用「變量名[下標]」的方式。下標的範圍是0~length。如果不在這個範圍內,程序會報「下標越界」異常。

用一張圖總結一下:

 

3.9.2數組的循環

       在實際運用中,經常會有遍曆數組的需求。上面我們用for演示過遍曆數組的情況。事實上,在Java5.0之後,有另外一種for循環的結構,可以非常方便的遍歷一個集合中的元素。代碼形式為:

for(類型 變量:集合){語句}

我們看一個例子:

int[] a = new int[] { 1, 2, 3, 4 };  
for (int i : a) {  
    System.out.println(i);  
} 

運行結果:

1  
2  
3  
4 

這種for循環可以理解為「遍歷集合中的每一個元素」。

3.9.3數組拷貝

       在實際工作中,還會經常碰到需要將一個數組中的全部或部分元素拷貝到另一個元素中的需求。如果是全量拷貝,有一個很簡單的辦法:

int[] a = new int[] { 1, 2, 3, 4 };  
int[] b = a;  

執行以上代碼後,數組b和數組a就一樣了。但是這樣有一個問題,我們繼續編寫代碼:

int[] a = new int[] { 1, 2, 3, 4 };  
int[] b = a;  
b[3] = 33;// 將數組的b的第4個元素賦值為33  
System.out.println(a[3]);// 結果數組a的第4個元素也變成33 

我們修改數組b的第4個元素,結果數組a的第4個元素也跟着一塊修改了。這是為什麼呢?這是因為Java中變量的的賦值,是引用賦值,用內存的表現來解釋可以一目了然:

 

把變量a賦值給變量b,實際上b和a將指向同一個內存地址。因此修改b的元素,實際上就是修改內存中的值,這樣a的元素自然也就跟着修改了。我們稱這種拷貝為「淺拷貝」。如果想要實現另外分配一塊內存空間給數組b,有沒有辦法呢?Java給我們提供了2種方法,一種是用System類的arraycopy方法,還有一種是Java6之後新提供的Arrays類的copyOf方法。具體怎麼用?還記得安裝JDK的時候,提到過的API文檔嗎?我們這時候就需要用到它了。(如果不記得了,回到那一節看一遍)。

       筆者的API文檔的路徑是:D:\Java大失叔\Java\jdk-8u261-docs-all\docs,我們找到api目錄下的index.html,用瀏覽器打開,可以看到首頁:

 

左上是所有的包,左下是當前包下的類,右邊是當前類的API說明。將來我們會經常用到API文檔來查找類的使用說明。我們先來看一下System的arraycopy方法。

System類在java.lang包下,我們定位到System類後,找到arraycopy方法,點擊方法名,可以進入該方法的詳細說明。我們摘抄方法體:

 

arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

這個方法的作用就是從源數組src的srcPos下標開始,拷貝length個元素到目標數組dest中,目標數組的起始下標為destPos。我們直接上代碼:

int[] a = new int[] { 1, 2, 3, 4 };  
int[] b = new int[4];  
System.arraycopy(a, 0, b, 0, 4);  
b[3] = 33;// 將數組的b的第4個元素賦值為33  
System.out.println(b[3]);// 數組b的第4個元素變成33  
System.out.println(a[3]);// 數組a的第4個元素仍然是4 

接下來,我們看一下Arrays.copyOf方法,Arrays類在java.util包下,util包提供了很多有用的工具類,Arrays是其中之一,我們會發現有很多copyOf方法,我們找到對應int型的:

 

copyOf(int[] original, int newLength)

這個方法的作用就是將源數組original的所有元素拷貝到一個新的數組中,可以指定新的數組的大小newLength,然後返回新的數組。如果newLength比源數組大小大,那麼新數組多餘的元素將被賦值為0,如果newLength比源數組大小小,那麼將只拷貝newLength個元素。我們上代碼:

int[] a = new int[] { 1, 2, 3, 4 };  
int[] b = Arrays.copyOf(a, 5);  
int[] c = Arrays.copyOf(a, 3);  
b[0] = 33;// 將數組的b的第4個元素賦值為33  
c[0] = 44;// 將數組的b的第4個元素賦值為33  
System.out.println(a[0]);// 數組a的第1個元素仍然是4  
System.out.println(b[0]);// 數組b的第1個元素變成33  
System.out.println(c[0]);// 數組c的第1個元素變成44 

3.9.4數組排序

       數組的排序也可以用Arrays類的sort方法,我們摘抄方法:

 

sort(int[] a)

這個方法對數組a進行升序排序。它內部採用的是優化的快速排序算法,這個算法對於大多數的數據集合來說效率都比較高。我們看一下代碼:

int[] a = new int[] { 1, 4, 2, 3 };  
Arrays.sort(a);  
for (int i : a) {  
    System.out.println(i);  
}  

運行結果:

1  
2  
3  
4 

排序之後,按照升序排列了。

Arrays類還有很多有用的方法,這裡就不一一列舉了,大家以後如果碰到需要對數組進行某些操作的時候,可以想到來查一下Arrays類,看看有沒有對應的方法。

3.9.5多維數組

       Java中還支持多維數組,但是其實在實際運用中很少用到,最多也就用一下二維數組,因此這裡只粗略的介紹一下二維數組。我們經常用到Excel表格,其實就可以看成一個二維數組,例如:

11

12

13

14

21

22

23

24

31

32

33

34

41

42

43

44

51

52

53

54

聲明二維數組、分配空間和賦值訪問和一維數組類似,我們用代碼演示:

int[][] table;// 聲明一個二維數組  
table = new int[5][4];// 分配空間5行4列  
table[0][1] = 12;// 第1行第2列賦值為12 

對於賦值,也可以和一維數組一樣,在聲明的同時就賦值:

int[][] table = new int[][] { { 11, 12, 13, 14 }, { 21, 22, 23, 24 } };// 聲明一個二維數組,並賦值  
int[][] table2 = { { 11, 12, 13, 14 }, { 21, 22, 23, 24 } };// 聲明一個二維數組,並賦值

二維數組其實可以看成是一個一維數組,然後該維度數組的每一個元素又是一個一維數組。用圖可以表示如下:

 

因此,聰明的你可能發現了,Java的二維數組中,數組的length的值是第一維度的大小。並且我們在分配二維數組大小的時候,可以只分配第一維度的大小,然後再給第一維度的數組的每一個元素分配不同的大小,例如:

int[][] table = new int[4][];// 只分配第一位維度的大小為4  
table[0] = new int[1];// 給table[0]分配大小為1  
table[0][0] = 11;  
table[1] = new int[] { 21, 22 };// 給table[1]分配大小為2,同時賦值  
table[2] = new int[] { 31, 32, 33 };// 給table[2]分配大小為3,同時賦值  
table[3] = new int[] { 41, 42, 43, 44 };// 給table[3]分配大小為4,同時賦值

這其實是一個不規則的二維數組。用表格表示如下圖:

11

 

 

 

21

22

 

 

31

32

33

 

41

42

43

44