HOWTO · Java

在 Java 中建立動態陣列

本指南教授如何在 Java 中建立動態陣列。

本頁內容

Java 陣列建立後長度固定。若要加入超出此長度的元素,可以將一般陣列包裝在 dynamic array 類別中,由類別追蹤目前存放的值數量,並在空間不足時更換底層陣列。以下範例儲存原始型別 int 值,只能在末尾新增元素、移除最後一個值,並可將儲存空間縮減至符合目前的 size。

在 Java 中使用自訂邏輯建立動態陣列

這個類別管理三項狀態:底層的 int[]、size 和 capacity。size 是目前儲存的值數量;capacity 則是底層陣列的長度。將兩者分開追蹤,陣列就能預留空間,而不會把預留槽位當成已儲存的值。

執行以下範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

import java.util.Arrays;

public class DynamicArray {
    private int[] values;
    private int size;

    public DynamicArray() {
        values = new int[2];
    }

    public void add(int value) {
        if (size == values.length) {
            int newCapacity = values.length == 0 ? 1 : values.length * 2;
            values = Arrays.copyOf(values, newCapacity);
        }
        values[size] = value;
        size++;
    }

    public void removeLast() {
        if (size == 0) {
            return;
        }
        size--;
        values[size] = 0;
    }

    public void shrinkToSize() {
        values = Arrays.copyOf(values, size);
    }

    public int size() {
        return size;
    }

    public int capacity() {
        return values.length;
    }

    @Override
    public String toString() {
        return Arrays.toString(Arrays.copyOf(values, size));
    }

    public static void main(String[] args) {
        DynamicArray numbers = new DynamicArray();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);
        numbers.add(50);
        System.out.println(numbers + " | size=" + numbers.size()
                + " | capacity=" + numbers.capacity());

        numbers.removeLast();
        System.out.println(numbers + " | size=" + numbers.size()
                + " | capacity=" + numbers.capacity());

        numbers.shrinkToSize();
        System.out.println(numbers + " | size=" + numbers.size()
                + " | capacity=" + numbers.capacity());
    }
}

輸出:

[10, 20, 30, 40, 50] | size=5 | capacity=8
[10, 20, 30, 40] | size=4 | capacity=8
[10, 20, 30, 40] | size=4 | capacity=4

新增五個值後,陣列中的值為 [10, 20, 30, 40, 50],size 為 5,capacity 為 8。呼叫 removeLast() 後,陣列中的值為 [10, 20, 30, 40],size 為 4,capacity 仍為 8。呼叫 shrinkToSize() 後,size 和 capacity 都是 4。範例輸出中的文字標籤維持英文。

了解大小、容量與調整大小

建構函式會將 capacity 設為 2、size 設為 0。每次呼叫 add 時,都會檢查 size 是否已達到 capacity。底層陣列滿了時,Arrays.copyOf 會配置一個長度加倍的新陣列,並將現有值複製過去。接著,新值會寫入索引 size 的位置,然後 size 加一。

以範例中的五次新增為例,capacity 會先從 2 增至 4,再增至 8。擴充時會複製已儲存的值;新陣列多出的槽位則可供後續新增使用。capacity 表示已配置的儲存空間,不是值的數量。toString() 方法只會複製索引小於 size 的部分,因此多出的槽位不會顯示為元素。

複製所需時間與複製的值數量成正比,而且複製期間兩個陣列都會暫時存在。容量加倍可避免每次新增都要複製,但個別新增若觸發擴充,仍須進行複製。這個小型類別用來示範運作原理;一般用途的集合通常使用 Java 的 ArrayList<Integer> 更合適。

新增與移除元素

add 只支援在末尾新增,不會在任意索引插入元素。removeLast 會將 size 減一,並把空出的槽位清為 Java 的 int 預設值 0。int 是原始型別,不是參照,因此清除槽位並非垃圾回收所必需;這麼做只是讓底層陣列未使用的部分維持可預期的狀態。當 size 為 0 時呼叫 removeLast,物件狀態不會改變。

移除元素不會自動縮減 capacity。若回收多出的陣列槽位比保留空間以供後續新增更重要,可呼叫 shrinkToSize()。縮減時也會將值複製到替換用的新陣列。若 size 為 0,capacity 會變成 0;下次呼叫 add 時,容量會先增至 1,再存入新值。此實作只儲存原始型別 int 值,且不提供索引存取、插入或移除中間元素的功能。