陣列是 Java 最基礎、最高效的容器,也是集合框架、序列化、JNI 互操作的底層支撐。本文系統梳理**記憶體模型、三種初始化模式、核心操作 API、經典演算法模板、陣列 vs 集合選型**五大板塊,並附贈生產級工具類與避坑清單。
| 特性 | 說明 | 影響 |
|---|---|---|
| 堆上分配 | new int[10] 在 Heap 分配連續記憶體 | 受 GC 管理、可觸發 Minor GC、大陣列直接進 Old Gen |
| 固定長度 | 創建後長度不可變 | 擴容需手動 Arrays.copyOf、System.arraycopy |
| 類型安全 | 編譯期強制檢查元素類型 | 避免 ClassCastException、JIT 優化友好 |
| 邊界檢查 | 每次存取執行 ArrayIndexOutOfBoundsException 檢查 | 安全但有微小開銷 (JIT 會消除熱點迴圈檢查) |
| 預設值 | 引用型 null、基本型 0/false/0.0 | 無需手動初始化、但語義上建議顯式賦值 |
length 欄位 | final、非方法、O(1) 存取 | 效能優於 Collection.size() |
// 編譯期確定內容,字節碼內嵌常量池
int[] primes = {2, 3, 5, 7, 11};
String[] weekDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
Color[] palette = {Color.RED, new Color(0x00FF00), Color.BLUE};
new int[]{...} + arraycopy。
// 1. 宣告引用
int[] scores;
// 2. 分配記憶體 (預設值 0)
scores = new int[100];
// 3. 迴圈賦值 (業務邏輯)
for (int i = 0; i < scores.length; i++) {
scores[i] = ThreadLocalRandom.current().nextInt(101);
}
// 僅分配記憶體,依賴 JVM 零填充
int[] arr = new int[10]; // 全 0
Object[] objs = new Object[10]; // 全 null
⚠️ **陷阱**:
int[] a = new int[3]; a[0] = 1; a[1] = 2; // a[2] 隱式為 0—— 若業務 0 有特殊含義 (如 ID=0 非法),**必須顯式初始化或用 -1 哨兵值**。
// 規則二維陣列 (矩形)
int[][] matrix = new int[3][4]; // 3 行 4 列,連續記憶體塊
// 鋸齒陣列 (每行長度不同) —— 節省記憶體、對應不規則數據
int[][] jagged = new int[3][];
jagged[0] = new int[5];
jagged[1] = new int[3];
jagged[2] = new int[2];
// 記憶體分佈:3 個獨立一維陣列 + 1 個引用陣列
| 操作 | API | 複雜度 | 備註 |
|---|---|---|---|
| 複製 | Arrays.copyOf(arr, newLen) | O(n) | 擴容/縮容首選,內部 System.arraycopy |
| 區間複製 | Arrays.copyOfRange(arr, from, to) | O(to-from) | 左閉右開 [from, to) |
| 排序 (基本型) | Arrays.sort(arr) | O(n log n) | Dual-Pivot QuickSort (JDK 7+) |
| 排序 (物件) | Arrays.sort(arr, Comparator) | O(n log n) | TimSort (穩定、適應性強) |
| 二分搜尋 | Arrays.binarySearch(arr, key) | O(log n) | **前提:已排序** |
| 填充 | Arrays.fill(arr, val) | O(n) | 重置陣列、哨兵值初始化 |
| 比較 | Arrays.equals(arr1, arr2) | O(n) | 深度比較用 Arrays.deepEquals |
| 轉字串 | Arrays.toString(arr) | O(n) | 除錯神器、避免 [I@15db9742 |
| 並行排序 | Arrays.parallelSort(arr) (JDK 8+) | O(n log n) / P | 大陣列 (>8192) 效能顯著提升 |
| 流式操作 | Arrays.stream(arr) | - | 函數式風格、配合 filter/map/reduce |
// 思路 1: 下標映射
int[] a = new int[100];
for (int i = 0; i < a.length; i++) a[i] = i + 1;
// 思路 2: 值驅動 (適用於非 0 起始)
int[] b = new int[100];
for (int v = 1; v <= 100; v++) b[v - 1] = v;
int[] fib = new int[20];
fib[0] = 1; fib[1] = 1;
for (int i = 2; i < fib.length; i++) {
fib[i] = fib[i-1] + fib[i-2];
}
void reverse(int[] a) {
for (int i = 0, j = a.length - 1; i < j; i++, j--) {
int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
}
}
// 返回新長度,前段為有效元素
int removeElement(int[] nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) nums[slow++] = nums[fast];
}
return slow;
}
// nums1 尾部有足夠空間,從後向前合併
void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1, j = n - 1, k = m + n - 1;
while (j >= 0) {
nums1[k--] = (i >= 0 && nums1[i] > nums2[j]) ? nums1[i--] : nums2[j--];
}
}
| 場景 | 推薦 | 理由 |
|---|---|---|
| 長度固定、高頻隨機存取、基本型 | 陣列 | 記憶體緊湊、無裝箱、JIT 向量化、最快 |
| 長度動態、頻繁增刪、業務模型豐富 | ArrayList | 封裝擴容、泛型安全、豐富 API |
| 首尾增刪、佇列/棧語義 | ArrayDeque | O(1) 兩端操作、無容量上限 |
| 高併發、無鎖、快照讀 | CopyOnWriteArrayList | 讀寫分離、迭代器不拋 CME |
| 基本型大量儲存、記憶體敏感 | 陣列 / 基本型集合庫 (Eclipse Collections, FastUtil) | 避免裝箱開銷、節省 50%+ 記憶體 |
int[] b = a; b[0]=99; // a[0] 也變成 99 —— 需獨立副本請用 Arrays.copyOf。Arrays.asList 返回固定大小列表:list.add() 拋 UnsupportedOperationException —— 需 new ArrayList<>(Arrays.asList(...))。List ✅,List ❌ —— 用 IntStream 或專用庫。new byte[10_000_000] 可能觸發 Full GC —— 預分配、分批、堆外記憶體。int[][] m = new int[1000][1000] 實際 1001 個物件 —— 扁平化 int[] flat = new int[1_000_000] 更緊湊。💡 核心心法:能用基本型陣列就別用包裝類集合;能用
Arrays標準庫就別手寫迴圈;動態長度首選ArrayList,固定長度首選陣列;大陣列注意 GC 壓力,考慮堆外或分塊。