本文整理自《編寫高質量代碼:改善 Java 程序的 151 個建議》第 5 章前半部份,聚焦陣列與集合效能核心議題:基本型別處理首選陣列、變長陣列技巧、淺拷貝風險、集合初始容量設定、最值演算法選型。
對基本型別大量運算,陣列效能遠超集合。
// 陣列求和:無裝箱/拆箱,棧上操作,效能最優
public static int sum(int[] datas) {
int sum = 0;
for (int i = 0; i < datas.length; i++) sum += datas[i];
return sum;
}
// List 求和:裝箱/拆箱開銷、堆上物件、效能差 10 倍
public static int sum(List<Integer> datas) {
int sum = 0;
for (int i = 0; i < datas.size(); i++) sum += datas.get(i); // 隱含拆箱
return sum;
}
核心差異:基本型別在棧上操作(快、容量小);包裝類型在堆上操作(慢、容量大)。實測基本型別求和,陣列效能是 List 的 10 倍。
注意:效能敏感場景(高頻計算、大數據處理),基本型別務必使用陣列。
Java 陣列定長,但可透過 Arrays.copyOf 實現擴容:
public static <T> T[] expandCapacity(T[] datas, int newLen) {
newLen = newLen < 0 ? 0 : newLen;
return Arrays.copyOf(datas, newLen); // 建立新陣列並拷貝
}
// 使用
Stu[] stuNums = new Stu[60];
stuNums = expandCapacity(stuNums, 80); // 擴容至 80
集合自動擴容底層原理相同。實務建議:確定大致資料量時優先預設容量,避免頻繁擴容拷貝。
Balloon[] box1 = new Balloon[7];
// 初始化 7 顆不同顏色氣球
Balloon[] box2 = Arrays.copyOf(box1, box1.length); // 淺拷貝!
box2[6].setColor(Color.Blue); // 修改 box2 最後一顆
// 發現 box1 最後一顆也變藍了!
原因:copyOf、clone()、集合 clone() 均為淺拷貝——基本型別拷貝值,參考型別拷貝引用地址。
解決:逐元素深拷貝(新建物件),或使用序列化/反序列化實現深克隆。
集合轉陣列拷貝再轉回集合,同樣面臨淺拷貝風險,務必謹慎。
ArrayList 擴容機制(JDK 7/8):
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍
// ... 檢查上限、拷貝
elementData = Arrays.copyOf(elementData, newCapacity);
}
Arrays.copyOf 完整陣列拷貝效能影響:大量資料插入時,多次擴容拷貝嚴重拖垮效能。
// 差:頻繁擴容
List<Item> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) list.add(new Item());
// 好:預設容量
List<Item> list = new ArrayList<>(100000);
for (int i = 0; i < 100000; i++) list.add(new Item());
同理適用於 HashMap(預設 16、負載因子 0.75)、StringBuilder 等。
| 演算法 | 程式碼 | 時間複雜度 | 適用場景 |
|---|---|---|---|
| 線性掃描 | 遍歷比較 | O(n) | 單次查找、資料無序、效能最高 |
| 排序後取值 | Arrays.sort() 取首/尾 | O(n log n) | 資料量 < 10000、程式碼簡潔優先 |
| TreeSet 去重排序 | new TreeSet(list).lower(last()) | O(n log n) | 需求第 2 大/小、去重、自動排序 |
public static int getSecond(Integer[] data) {
List<Integer> list = Arrays.asList(data);
TreeSet<Integer> ts = new TreeSet<>(list); // 去重 + 升序
return ts.lower(ts.last()); // 小於最大值的最大值
}
利用 TreeSet 紅黑樹特性:自動去重、有序、提供 lower/higher 導航方法,程式碼極簡且正確處理重複值。
這五個建議揭示了 Java 效能優化的核心邏輯:理解底層資料結構與記憶體模型,在正確層級做正確選擇。