Java SE 6 技術手冊

5.1 一維陣列、二維陣列

不管在其它語言中是如何,陣列在 Java 中可得看作一個物件,它有一些值得探討的特性,這個小節會先介紹最常使用的一維陣列與二維陣列。

5.1.1 一維陣列物件

您現在要整理全班的 Java 小考成績,您希望寫個小程式,全班共有 40 名學生,所以您必須有 40 個變數來儲存學生的成績,現在問題來了,根據第 3 章學過的變數宣告方式,難道您要宣告 40 個名稱不同的變數來儲存學生的成績資料嗎?

當然不必這麼麻煩,Java 提供「陣列」(Array)讓您可以宣告一個以「索引」(Index)作為識別的資料結構,在 Java 中,您可以這麼宣告一個陣列並初始陣列內容:

int[] score = {90, 85, 55, 94, 77};

這個程式片段宣告了一個 score 陣列,它的內容包括 90、85、55、94 與 77 這五個元素,您要存取陣列時,必須使用索引值來指定存取陣列中的哪個元素,在Java 中陣列的索引是由0開始,也就是說索引 0 的位置儲存 90、索引 1 的位置儲存 85、索引 2 的位置儲存 55,依此類推,如果您要循序的取出陣列中的每個值並顯示出來,您可以使用 for 迴圈,如範例 5.1 所示。

範例 5.1 SimpleArray.java

public class SimpleArray {
    public static void main(String[] args) {
        int[] score = {90, 85, 55, 94, 77};

        for(int i = 0; i < score.length; i++) 
            System.out.printf("score[%d] = %d\n", i, score[i]); 
    }
}

在範例 5.1 中,在每次的 i 遞增後,都會作為陣列的索引指定以取出對應的陣列值,執行的結果如下:

score[0] = 90
score[1] = 85
score[2] = 55
score[3] = 94
score[4] = 77

在存取陣列元素時,必須注意到您指定的索引值不可超出陣列範圍,例如在範例 5.1 中,陣列最多可以索引到 4,所以您不可以存取超過 4 的索引值,否則會發生 ArrayIndexOutOfBoundsException 例外,如果您不處理這個例外,程式將會終止,第 10 章會詳細說明例外處理的方法。

範例 5.1 中使用了 length 這個陣列物件的屬性成員,在 Java 中陣列是一個物件,而不是單純的資料集合,陣列物件的 length 屬性成員可以取回陣列的長度,也就是陣列中的元素個數。

當您宣告一個陣列時,其實就是在配置一個陣列物件,實際上範例 5.1 中示範的只是陣列宣告與初始化成員的一個簡易宣告方式,在 Java 中物件都是以 new 來配置記憶體空間,陣列的使用也不例外,事實上一個完整的陣列宣告方式如下所示:

int[] arr = new int[10];

在上面的宣告中,arr 是個 int[] 型態的參考名稱,程式會為 arr 配置可以儲存 10 個 int 整數的一維陣列物件,索引為 0 到 9,初始值預設為 0,在 Java 中配置陣列之後,若還沒有指定初值,則依資料型態的不同,會預設有不同的初值,如表 5.1 所示。

表 5.1 陣列元素初始值

資料型態 初始值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char \u0000
boolean false

範例 5.2 使用配置的語法來宣告陣列,並使用 for 迴圈來設定每個元素的值然後顯示出來。

範例 5.2 ArrayDemo.java

public class ArrayDemo {
    public static void main(String[] args) {
        int[] arr = new int[10];

        System.out.print("arr 初始值: "); 
        for(int i = 0; i < arr.length; i++) { 
            System.out.print(arr[i] + " "); 
            arr[i] = i; 
        }

        System.out.print("\narr 設定值: "); 
        for(int i = 0; i < arr.length; i++) 
            System.out.print(arr[i] + " "); 
        System.out.println(); 
    }
}

執行結果:

arr 初始值: 0 0 0 0 0 0 0 0 0 0
arr 設定值: 0 1 2 3 4 5 6 7 8 9

如果您想要在使用 new 新增陣列時一併指定初始值,則可以如下撰寫,注意這個方式不必指定陣列長度:

int[] score = new int[] {90, 85, 55, 94, 77};

事實上這個宣告方式是範例 5.1 中 score 陣列宣告的完整形式,範例 5.3 的是改寫範例 5.1 中 score 宣告方式之後的結果,執行結果與範例 5.1 則是相同的。

範例 5.3 SimpleArray2.java

public class SimpleArray2 {
    public static void main(String[] args) {
        int[] score = new int[] {90, 85, 55, 94, 77};

        for(int i = 0; i < score.length; i++) 
            System.out.printf("score[%d] = %d\n", i, score[i]); 
    }
}

由於陣列的記憶體空間是使用 new 配置而來,這意味著您也可以使用動態的方式來宣告陣列長度,而不用在程式中事先決定陣列大小,範例 5.4 示範了如何由使用者的輸入來決定陣列長度,它是一個計算輸入分數平均的程式。

範例 5.4 AverageInput.java

import java.util.Scanner;

public class AverageInput {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("請輸入學生人數: "); 

        int length = scanner.nextInt();
        float[] score = new float[length];  // 動態配置長度 

        for(int i = 0; i < score.length; i++) {
            System.out.print("輸入分數:");
            float input = scanner.nextFloat();
            score[i] = input;
        }

        System.out.print("\n分數:");
        float total = 0;
        for(int i = 0; i < score.length; i++) {
            total = total + score[i];
            System.out.print(score[i] + " ");
        }

        System.out.printf("\n平均:%.2f", total / score.length);
    }
}

執行結果:

請輸入學生人數: 3
輸入分數:88.3
輸入分數:76.2
輸入分數:90.0

分數:88.3 76.2 90.0
平均:84.83

在範例 5.4 中,您先宣告一個陣列參考名稱 score,使用 float[] score 表示 score 名稱將參考至一個元素為 float 的一維陣列物件,在使用者輸入指定長度後,您使用這個長度來配置陣列物件,並將這個陣列物件指定給 score 名稱來參考。

良葛格的話匣子 您也可以使用像是 int arr[] 這樣的方式來宣告陣列,這種宣告方式源於 C/C++ 中對陣列宣告的語法,不過在 Java 中建議使用 int[] arr 這樣的宣告方式,這也表明了 arr 是個 int[] 型態的參考名稱。

陣列的索引值由 0 開始並不是沒有原因的,事實上索引值表示的是:所指定的陣列元素相對於陣列第一個元素記憶體位置的位移量(Offset)。索引為 0 表示位移量為 0,所以就是指第一個元素,而索引 9 就是指相對於第一個元素的位移量為 9。不過在 Java 中您不直接處理關於記憶體位址的操作,以上的觀念主要是讓您更瞭解一下陣列索引的運作原理。

5.1.2 二維陣列物件

一維陣列使用「名稱」與「一個索引」來指定存取陣列中的元素,您也可以宣告二維陣列,二維陣列使用「名稱」與「兩個索引」來指定存取陣列中的元素,其宣告方式與一維陣列類似:

int[][] arr = {{1, 2, 3}, 
               {4, 5, 6}};

從上面這個程式片段來看,就可以清楚的看出二維陣列的索引方式,您宣告了 2 列(Row)3 行(Column)的陣列,使用 { } 與適當的斷行可以協助您指定陣列初值,範例 5.5 簡單的示範二維陣列的存取。

範例 5.5 TwoDimArray.java

public class TwoDimArray {
    public static void main(String[] args) {
        int[][] arr =     {{1, 2, 3}, 
                            {4, 5, 6}}; 

        for(int i = 0; i < arr.length; i++) { 
            for(int j = 0; j < arr[0].length; j++) 
                System.out.print(arr[i][j] + " "); 
            System.out.println(); 
        } 
    }
}

執行結果:

1 2 3
4 5 6

陣列值 arr[i][j] 表示指定的是第 i 列第 j 行的值。在使用二維陣列物件時,注意 length 所代表的長度,陣列名稱後直接加上 length(如 arr.length),所指的是有幾列(Row);指定索引後加上 length(如 arr[0].length),指的是該列所擁有的元素,也就是行(Column)數目。

良葛格的話匣子 初學者對於二維陣列的瞭解到這邊就可以了,接下來的內容是進階的二維陣列說明,初學者可以先試著瞭解,如果覺得觀念上有點難,可以先跳過待以後物件觀念更清楚後,再來看接下來的內容。

要瞭解範例 5.5中 length 成員各代表哪一個長度,您必須從物件配置的角度來瞭解。以物件的方式來配置一個二維陣列物件,您要使用以下的語法:

int[][] arr = new int[2][3];

上面這個程式片段中,您配置了 2 列 3 行的二維陣列物件,由於陣列元素的資料型態是 int,所以陣列元素的預設元素為 0。

來細究一下二維陣列的配置細節,其實 arr[0]、arr[1] 是參考名稱,分別參考至兩個 int[] 型態的物件,其長度各為 3,而 arr 名稱的型態是 int[][],參考至 int[][] 型態的物件,物件中包括 arr[0] 與 arr[1] 兩個名稱,其關係如圖 5.1 所示。

二維陣列的配置關係

圖 5.1 二維陣列的配置關係

從圖 5.1 中您可以看到,arr 參考至 int[][] 形態的物件,而 arr[0] 與 arr[1] 再分別參考至一個 int[] 物件,所以範例 5.5 中,使用的 arr.length 得到的是 2,而 arr[0].length 得到的長度是 3。有了陣列配置的觀念,您可以改寫範例 5.5 為範例 5.6,讓程式中的 int[] 型態之名稱 foo 來循序取出 arr[0] 與 arr[1] 所參考的 int[] 物件中的每個元素值,執行結果是相同的。

範例 5.6 TwoDimArray2.java

public class TwoDimArray2 {
    public static void main(String[] args) {
        int[][] arr = {{1, 2, 3}, 
                          {4, 5, 6}};

        int[] foo = arr[0]; // 將arr[0] 所參考的陣列物件指定給foo

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

        foo = arr[1]; // 將arr[1] 所參考的陣列物件指定給foo

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

如果在使用 new 配置二維陣列後想要一併指定初值,則可以如下撰寫:

int[][] arr = new int[][] {{1, 2, 3}, 
                           {4, 5, 6}};

同樣的道理,您也可以宣告三維以上的陣列,如果要同時宣告初始元素值,可以使用以下的簡便語法:

int[][][] arr = {
                   {{1, 2, 3}, {4, 5, 6}}, 
                   {{7, 8, 9}, {10, 11, 12}}
                };

上面這個程式片段所宣告的三維陣列是 2x2x3,您將之想為兩面 2x3 二維陣列交疊在一起就是了,每一面的元素如圖 5.2 所示。

三維陣列的配置關係

圖 5.2 三維陣列的配置關係

如果要動態宣告三維陣列,就使用以下的語法:

int[][][] arr = new int[2][2][3]; 

比三維以上的更多維陣列之宣告,在Java中也是可行的,但並不建議使用,使用多維陣列會讓元素索引的指定更加困難,此時適當的將資料加以分割,或是使用其它的資料結構來解決,會比直接宣告多維陣列來得實在。

由以上的說明,接下來討論「不規則陣列」。陣列的維度不一定要是四四方方的,您也可以製作一個二維陣列,而每個維度的長度並不相同,範例 5.7 是個簡單的示範。

範例 5.7 TwoDimArray3.java

public class TwoDimArray3 {
    public static void main(String[] args) {
        int arr[][]; 

        arr = new int[2][]; 
        arr[0] = new int[3]; // arr[0] 參考至長度為3的一維陣列
        arr[1] = new int[5]; // arr[1] 參考至長度為5的一維陣列

        for(int i = 0; i < arr.length; i++) { 
            for(int j = 0; j < arr[i].length; j++) 
                arr[i][j] = j + 1; 
        } 

        for(int i = 0; i < arr.length; i++) { 
            for(int j = 0; j < arr[i].length; j++) 
                System.out.print(arr[i][j] + " "); 
            System.out.println(); 
        } 
    }
}

這個例子只是先前說明之觀念延伸,在這個例子中,陣列第一列的長度是3,而第二列的長度是 5,執行結果如下:

1 2 3
1 2 3 4 5

良葛格的話匣子 在宣告二維陣列時,也可以使用 int arr[][] 這樣的宣告方式,這種宣告方式源於C/C++中對陣列宣告的語法,不過在 Java 中建議使用 int[][] arr 這樣的宣告方式,這也表示了 arr 是個 int[][] 型態的參考名稱;同樣的,您也可以使用 int arr[][][] 這樣的方式來宣告三維陣列,但鼓勵您使用 int[][][] arr 的宣告方式。