陣列基礎


若要用程式記錄Java小考成績,若有10名學生,只使用變數的話,必須有40個變數儲存學生成績:

int score1 = 88;
int score2 = 81;
int score3 = 74;
...
int score10 = 93;

實際上不可能這麼作,陣列基本上是用來收集資料,是具有索引(Index)的資料結構,在Java中要宣告陣列並初始值,可以如下:

int[] scores = {88, 81, 74, 68, 78, 76, 77, 85, 95, 93};

這個程式片段建立了一個陣列,因為使用int[]宣告,所以會在記憶體中分配長度為10的int連續空間,各個空間儲存了88、81、74、68、78、76、77、85、95、93,各個空間都給予索引編號,索引由0開始,由於長度是10,所以最後一個索引為9,如果存取超出索引範圍,就會拋出ArrayIndexOutOfBoundsException的錯誤。

宣告陣列時,就Java開發人員的撰寫慣例來說,建議將[]放在型態關鍵字之後。[]也可以放在宣告的名稱之後,在過去,這是為了讓C/C++開發人員看來比較友善(目前來說已不建議):

int scores[] = {88, 81, 74, 68, 78, 76, 77, 85, 95, 93};

有關Java程式寫作的一些慣例,可以參考 java-coding-standards。

如果想要循序地取出陣列中每個值,方法之一是使用for迴圈:

package cc.openhome;

public class Score {
    public static void main(String[] args) {
        int[] scores = {88, 81, 74, 68, 78, 76, 77, 85, 95, 93};
        for(int i = 0; i < scores.length; i++) {
            System.out.printf("學生分數:%d %n", scores[i]);
        }
    }
} 

在宣告的參考名稱旁加上[]並指定索引,就可以取得對應值,上例從i為0到9,逐一取得值並顯示出來。執行結果如下:

學生分數:81
學生分數:74
學生分數:68
學生分數:78
學生分數:76
學生分數:77
學生分數:85
學生分數:95
學生分數:93

在Java中陣列是物件,而不是單純的資料集合,陣列的length屬性可以取得陣列長度,也就是陣列的元素個數。

在上面這個範例中,實際上並沒有真正需要索引值,索引只是從頭遞增到尾。如果需求是循序地從頭至尾取出陣列值,從JDK5之後,有了更方便的增強式for迴圈(Enhanced for loop)語法:

for(int score : scores) {
    System.out.printf("學生分數:%d %n", score);
}

這個程式片段會取得scores陣列第一個元素,指定給score變數後執行迴圈本體,接著取得scores中第二個元素,指定給score變數後執行迴圈本體,依此類推,直到scores陣列中所有元素都走訪完為止。將這段for迴圈片段,取代Score類別中的for迴圈,執行結果相同。實際上,增強式for迴圈是編譯器蜜糖,將位元碼反組譯後可以看到,還是使用索引方式來走訪陣列:

int ai[] = {88, 81, 74, 68, 78, 76, 77, 85, 95, 93};
int ai1[] = ai;
int i = ai1.length;
for(int j = 0; j < i; j++) {
    int k = ai1[j];
    ...
}

如果要設定值給陣列中某個元素,也是透過索引。例如:

scores[3] = 86;
System.out.println(scores[3]);

上面這個程式片段將陣列中第4個元素(因為索引從0開始,索引3就是第4個元素)結指定為86,所以會顯示86的結果。

一維陣列使用一個索引存取陣列元素,你也可以宣告二維陣列,二維陣列使用兩個索引存取陣列元素。例如宣告陣列來儲存XY座標位置要放的值:

package cc.openhome;

public class XY {
    public static void main(String[] args) {
        int[][] cords = {
            {1, 2, 3},
            {4, 5, 6}
        };
        for(int x = 0; x < cords.length; x++) {
            for(int y = 0; y < cords[x].length; y++) {
                System.out.printf("%2d", cords[x][y]);
            }
            System.out.println();
        }
    }
} 

要宣告二維陣列,就是在型態關鍵字旁加上[][],初學者暫時將二維陣列看作方陣會比較容易理解,由於有兩個維度,必須先透過cords.length得知有幾列(Row),對於每一列,再利用cords[x].length得知每列有幾個元素,由於在這個範例中,是用二維陣列來記錄x、y座標的儲存值,x、y就相當於列、行(Column)索引,因此可使用cords[x][y]來取得x、y座標的儲存值。執行結果如下:

1 2 3
4 5 6

其實這個範例也是循序地走訪二維陣列,並沒有真正要用索引作什麼事,可以用增強式for迴圈來改寫會比較簡潔:

for(int[] row : cords) {
    for(int value : row) {
        System.out.printf("%2d", value);
    }
    System.out.println();
}

將這個程式片段,取代XY類別中的for迴圈,執行結果相同,但第一個for中的int[] row : cords是怎麼回事?如果你想知道答案,就得認真瞭解陣列是物件這件事,而不僅僅將它當作連續記憶體空間...這之後還會詳加探討…

如果要宣告三維陣列,就是在型態關鍵字旁使用[][][],四維就是[][][][],依此類推,不過不建議以三維陣列以上方式記錄資料,因為不容理撰寫、閱讀與理解,自定類別來解決這類需求會是更好的方式。