前言
雖然在處理物件型態時,我們常會依據不同情境選擇 ArrayList 或 LinkedList;但對於基礎型態(Primitive Types)而言,傳統陣列依然具有強大優勢。由於陣列在記憶體中是連續配置的,且不具備物件型態的額外開銷(Overhead),因此能提供更高的存取效能。
宣告陣列
以下指令宣告元素型態為 double 的一維陣列:
double vec[];
傳統陣列最核心的優勢在於記憶體空間的連續性,這能帶來極佳的存取效能。
我們也可以宣告多維陣列。以下指令宣告元素型態為 double 的二維陣列:
double mtx[][];
⚠️ 記憶體關鍵觀念:
值得注意的是,Java 的多維陣列本質上是「陣列的陣列(Array of Arrays)」。這意味著 mtx 每一列(Row)的一維陣列,在記憶體中通常是分散且不連續的。雖然每一列內部的元素依然連續,但整張二維矩陣在記憶體中並非完美的一體成型。
在進行大型科學運算或需要極致效能的場景時,必須特別留意這個特性所帶來的 CPU 快取(Cache Miss)開銷。
實作記憶體連續的二維陣列
為了克服 Java 多維陣列不連續的問題,在影像處理或科學運算等追求極致效能的場景中,業界標準作法是將二維矩陣「扁平化(Flatten)」為一維陣列。
我們可以根據列(Rows)與行(Cols)的乘積總和,宣告一個真正連續的一維陣列。存取元素時,透過「列索引 * 總行數 + 行索引」的公式計算出對應的一維索引值。參考以下程式碼片段:
int rows = 3;
int cols = 4;
double[] mtx = new double[rows * cols];
double value = mtx[1 * cols + 2];
透過這種設計,整張矩陣在記憶體中會呈現連續的排列,能完美契合 CPU 快取機制(Cache Locality),大幅提升迴圈運算時的執行效率。
建立陣列
使用保留字 new 來建立陣列。以下指令建立元素型態為 double、容量為 10 的一維陣列:
double vec[] = new double[10];
以下指令建立元素型態為 double、維度分別為 3、2 的二維陣列:
double mtx[][] = new double[3][2];
也可以建立陣列的陣列 (array of array)。參考以下指令:
/* Initialize the outer layer
of the 2D array. */
double mtx[][] = new double[3][];
/* Initialize the inner layer
of the 2D array. */
for (int i = 0; i < mtx.length; ++i) {
mtx[i] = new double[2];
}
以下指令使用陣列實字 (array literal) 來建立陣列並對其賦值:
double vec[] = { 1, 2, 3, 4, 5 };
同理,也可以用在二維陣列上:
double mtx[][] = {
{ 1, 2 },
{ 3, 4 },
{ 5, 6 }
};
存取陣列元素
陣列使用非負整數做為索引來存取元素。陣列索引的起始值為 0 而非 1,這是因為陣列索引是陣列元素的偏移值 (offset)。
參考以下範例:
public class MainProgram
{
public static void main(String[] args)
{
int arr[] = {1, 2, 3, 4, 5};
assertCond(5 == arr[4]);
}
/* Home-made assertion. */
public static void assertCond(boolean cond)
{
if (!cond)
throw new IllegalArgumentException("Wrong condition");
}
}
走訪陣列
比較傳統的方式是使用計數器 (counter) 來走訪陣列元素:
int arr[] = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; ++i)
System.out.println(arr[i]);
由於 Java 的陣列有儲存陣列長度的資訊,使用計數器會比在 C 或 C++ 簡單。
也可以使用隱式的迭代器 (iterator) 來走訪:
int arr[] = {1, 2, 3, 4, 5};
for (int n : arr)
System.out.println(n);
動態增減陣列元素
陣列無法增減長度。雖然程式設計者可以使用資料結構的概念來製作動態陣列 (dynamic array),Java 標準物件庫已經有 ArrayList 了,程式設計者不應該重造次等輪子。