HOWTO · Java

Java에서 동적 배열 만들기

이 가이드에서는 Java에서 동적 배열을 만드는 방법을 설명합니다.

이 페이지의 내용

Java 배열은 만든 뒤 길이를 바꿀 수 없습니다. 그 길이보다 많은 요소를 추가하려면 일반 배열을 감싸는 동적 배열 클래스를 만들고, 저장된 값의 개수를 추적하며 공간이 부족할 때 내부 배열을 더 큰 배열로 교체하면 됩니다. 아래 예제는 기본형 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]이 들어 있고, 크기는 5, 용량은 8입니다. removeLast()를 호출한 뒤에는 [10, 20, 30, 40]이 남으며, 크기는 4, 용량은 8입니다. shrinkToSize()를 호출하면 크기와 용량이 모두 4가 됩니다.

크기, 용량 및 크기 조정 이해하기

생성자는 용량을 2, 크기를 0으로 시작합니다. add를 호출할 때마다 크기가 용량에 도달했는지 확인합니다. 내부 배열이 가득 차면 Arrays.copyOf가 길이가 두 배인 새 배열을 할당하고 기존 값을 복사합니다. 그런 다음 다음 값을 size 인덱스에 기록하고 크기를 1 늘립니다.

예제에서 값을 다섯 번 추가하면 용량은 2에서 4로, 다시 8로 바뀝니다. 용량을 늘리는 과정에서 저장된 값이 복사되고, 새 배열의 남는 칸은 이후 추가 작업에 사용할 수 있습니다. 용량은 할당된 저장 공간을 나타내며, 값의 개수를 뜻하지 않습니다. toString() 메서드는 크기 이전의 부분만 복사하므로 남는 칸은 요소로 표시되지 않습니다.

복사에 걸리는 시간은 복사하는 값의 개수에 비례하며, 복사 중에는 두 배열이 일시적으로 모두 메모리에 있어야 합니다. 용량을 두 배씩 늘리면 추가할 때마다 복사하지 않아도 되지만, 용량을 늘려야 하는 개별 추가 작업에서는 여전히 복사가 발생합니다. 이 작은 클래스는 동작 원리를 보여 주기 위한 예제입니다. 일반적인 컬렉션에는 보통 Java의 ArrayList<Integer>가 더 적합합니다.

요소 추가 및 제거

add는 끝에 값을 추가하는 기능만 지원하며, 임의의 인덱스에 삽입하지는 않습니다. removeLast는 크기를 1 줄이고, 비워진 칸을 Java의 기본 int 값인 0으로 설정합니다. int는 참조가 아니라 기본형 값이므로, 이 칸을 지워도 가비지 컬렉션에는 영향을 주지 않습니다. 대신 내부 배열의 미사용 부분을 예측 가능한 상태로 유지합니다. 크기가 0일 때 removeLast를 호출하면 객체의 상태는 바뀌지 않습니다.

제거해도 용량은 자동으로 줄어들지 않습니다. 앞으로 추가할 공간을 남겨 두는 것보다 배열의 여유 칸을 회수하는 일이 더 중요할 때 shrinkToSize()를 호출하세요. 이 메서드도 값을 새 배열로 복사합니다. 크기가 0이면 용량은 0이 되고, 다음에 add를 호출하면 새 값을 저장하기 전에 용량을 1로 늘립니다. 이 구현은 기본형 int 값만 저장하며 인덱스 접근, 삽입, 중간 요소 제거 기능은 제공하지 않습니다.