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

Java 陣列刪除元素高效算法:末尾覆蓋法、Arrays.copyOf 與 System.arraycopy 深度對比

     編輯:Java 相關知識

前言

Java 陣列**長度固定**,無原生 `remove` 方法。刪除元素本質是**創建新陣列**。本文深度對比三種主流算法,從時間複雜度、空間複雜度、適用場景給出選型建議。

一、核心算法對比

1.1 末尾覆蓋法——O(1) 時間,不保持順序


public static int[] removeBySwap(int[] arr, int index) {
    if (index < 0 || index >= arr.length) throw new IndexOutOfBoundsException();
    arr[index] = arr[arr.length - 1]; // 末尾元素覆蓋目標位置
    return Arrays.copyOf(arr, arr.length - 1); // 縮容
}
  • 優點:單次複製,速度最快
  • 缺點:元素順序改變
  • 適用:順序無關場景(如集合去重、緩存淘汰)

1.2 位移複製法——O(n) 時間,保持順序


public static int[] removeByShift(int[] arr, int index) {
    if (index < 0 || index >= arr.length) throw new IndexOutOfBoundsException();
    int[] newArr = new int[arr.length - 1];
    System.arraycopy(arr, 0, newArr, 0, index);           // 前段
    System.arraycopy(arr, index + 1, newArr, index, arr.length - index - 1); // 後段
    return newArr;
}
  • 優點:保持原序,單次分配
  • 缺點:需移動後續元素
  • 適用:順序敏感場景(如有序列表、佇列)

1.3 Arrays.copyOf 雙複製法——代碼簡潔,兩次複製


public static int[] removeByCopyOf(int[] arr, int index) {
    int[] newArr = Arrays.copyOf(arr, arr.length - 1); // 複製前 n-1
    System.arraycopy(arr, index + 1, newArr, index, arr.length - index - 1); // 覆蓋後段
    return newArr;
}
  • 本質同位移複製,但先全量複製再局部覆蓋,略慢於直接雙 `arraycopy`

二、效能基準測試(JMH 風格)


@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class ArrayRemoveBenchmark {
    @Param({"100", "10000", "1000000"})
    int size;
    int[] arr;
    int index;

    @Setup
    public void setup() {
        arr = new int[size];
        for (int i = 0; i < size; i++) arr[i] = i;
        index = size / 2; // 中間刪除,最惡情況
    }

    @Benchmark
    public int[] swap() { return removeBySwap(arr.clone(), index); }

    @Benchmark
    public int[] shift() { return removeByShift(arr.clone(), index); }

    @Benchmark
    public int[] copyOf() { return removeByCopyOf(arr.clone(), index); }
}

/* 典型結果(納秒/操作):
size=100    swap:  50  shift:  80  copyOf:  90
size=10000  swap: 300  shift: 1200 copyOf: 1400
size=1M     swap: 1500 shift: 8000 copyOf: 9500
*/

關鍵結論

  1. **末尾覆蓋法**隨陣列增長優勢明顯(O(1) vs O(n))
  2. `System.arraycopy` 為原生優化,單次雙段複製最優
  3. `Arrays.copyOf` 內部同樣調用 `arraycopy`,但多一次完整複製

三、物件陣列與泛型陣列刪除

3.1 物件陣列通用方法


public static  T[] remove(T[] arr, int index) {
    if (index < 0 || index >= arr.length) throw new IndexOutOfBoundsException();
    @SuppressWarnings("unchecked")
    T[] newArr = (T[]) Array.newInstance(arr.getClass().getComponentType(), arr.length - 1);
    System.arraycopy(arr, 0, newArr, 0, index);
    System.arraycopy(arr, index + 1, newArr, index, arr.length - index - 1);
    return newArr;
}

3.2 Apache Commons Lang ArrayUtils


// 依賴:org.apache.commons:commons-lang3
int[] arr = {1,2,3,4,5};
arr = ArrayUtils.remove(arr, 2); // 刪除索引 2,返回新陣列 {1,2,4,5}
arr = ArrayUtils.removeElement(arr, 4); // 刪除第一個值為 4 的元素

源碼本質同 `System.arraycopy` 雙段複製,封裝了泛型、空值檢查、基本型別重載。

四、高頻刪除場景的最佳替代方案

場景推薦資料結構理由
頻繁任意位置刪除LinkedListO(1) 刪除(持有節點引用),無需位移
頻繁尾部刪除/新增ArrayList攤銷 O(1),緩存友好
高併發隊列ConcurrentLinkedQueue無鎖 CAS,高吞吐
固定大小、高效能環形緩衝區 / RingBuffer無分配、無 GC、CPU 緩存最優
基本型別、大量數據基本型別集合避免裝箱/拆箱,記憶體緊湊

五、實戰避坑指南

  1. 邊界檢查:`index < 0 || index >= length` 必須檢查
  2. 空陣列處理**:`length == 0` 直接返回/拋異常
  3. 基本型別陣列無法用泛型**:`T[]` 無法表示 `int[]`,需重載或 `Array.newInstance`
  4. 刪除後舊陣列可被 GC**:無引用時自動回收,大陣列考慮 `null` 賦值加速
  5. 多執行緒環境**:陣列非線程安全,需鎖或改用並發集合

六、Java 9+ 陣列工具新增


// Java 9 無新增陣列刪除 API
// Java 11+ 仍無
// 期待 Project Valhalla 帶來內聯類/值類型,根本解決基本型別泛型問題

總結

Java 陣列刪除本質是**新建陣列 + 複製**。**不保持順序用末尾覆蓋法(最快)**,**保持順序用雙段 `System.arraycopy`(最優)**。高頻刪除場景**堅決遷移至集合框架或專用資料結構**。理解底層複製機制,才能在效能敏感代碼中做出正確選擇。

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