首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

高品質 Java 程式碼建議 60-64:陣列效能優勢、變長陣列、淺拷貝陷阱、初始容量與最值演算法

     編輯:Java 綜合教學

高品質 Java 程式碼建議 60-64:陣列效能優勢、變長陣列、淺拷貝陷阱、初始容量與最值演算法

本文整理自《編寫高質量代碼:改善 Java 程序的 151 個建議》第 5 章前半部份,聚焦陣列與集合效能核心議題:基本型別處理首選陣列、變長陣列技巧、淺拷貝風險、集合初始容量設定、最值演算法選型。

建議 60:效能考量,陣列是首選

對基本型別大量運算,陣列效能遠超集合。

// 陣列求和:無裝箱/拆箱,棧上操作,效能最優
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 倍。

注意:效能敏感場景(高頻計算、大數據處理),基本型別務必使用陣列。

建議 61:必要時使用變長陣列

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

集合自動擴容底層原理相同。實務建議:確定大致資料量時優先預設容量,避免頻繁擴容拷貝。

建議 62:警惕陣列淺拷貝

Balloon[] box1 = new Balloon[7];
// 初始化 7 顆不同顏色氣球
Balloon[] box2 = Arrays.copyOf(box1, box1.length); // 淺拷貝!
box2[6].setColor(Color.Blue); // 修改 box2 最後一顆
// 發現 box1 最後一顆也變藍了!

原因:copyOf、clone()、集合 clone() 均為淺拷貝——基本型別拷貝值,參考型別拷貝引用地址。

解決:逐元素深拷貝(新建物件),或使用序列化/反序列化實現深克隆。

集合轉陣列拷貝再轉回集合,同樣面臨淺拷貝風險,務必謹慎。

建議 63:明確場景下為集合指定初始容量

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);
}
  • 預設初始容量:10
  • 擴容倍數:1.5 倍(Vector 為 2 倍)
  • 每次擴容觸發 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 等。

建議 64:多種最值演算法,適時選擇

演算法程式碼時間複雜度適用場景
線性掃描遍歷比較O(n)單次查找、資料無序、效能最高
排序後取值Arrays.sort() 取首/尾O(n log n)資料量 < 10000、程式碼簡潔優先
TreeSet 去重排序new TreeSet(list).lower(last())O(n log n)需求第 2 大/小、去重、自動排序

實測對比(基本型別陣列)

  • 資料量 < 10000:線性掃描與排序取值差異微小(同毫秒級)
  • 資料量 > 10000:線性掃描明顯更快

尋找「老二」(次大值)最佳實踐

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 導航方法,程式碼極簡且正確處理重複值。

總結

  1. 基本型別大量運算 → 陣列(避免裝箱/拆箱,棧上運算)
  2. 動態長度 → Arrays.copyOf 擴容或集合(預設容量關鍵)
  3. 拷貝參考型別 → 警惕淺拷貝(必要時深拷貝)
  4. 集合大量資料 → 預設初始容量(減少擴容拷貝)
  5. 最值查找 → 線性掃描最快,TreeSet 功能最全(按場景選型)

這五個建議揭示了 Java 效能優化的核心邏輯:理解底層資料結構與記憶體模型,在正確層級做正確選擇。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved