본문 바로가기
Development/Java

[Java] System.arraycopy vs Arrays.copyOfRange 차이점 비교 - 성능과 사용법

by 은스타 2025. 3. 25.
반응형
System.arraycopy vs Arrays.copyOfRange 차이점 비교 - 성능과 사용법

System.arraycopy vs Arrays.copyOfRange 차이점 비교 - 성능과 사용법

Java에서 배열을 복사할 때 자주 사용하는 두 메서드인 System.arraycopyArrays.copyOfRange는 얼핏 보기에 비슷한 기능을 수행하지만, 내부 구현과 사용 방법, 그리고 성능 측면에서 중요한 차이가 있습니다.

System.arraycopy는 네이티브 메서드로 C언어로 구현되어 있어 매우 빠른 성능을 제공하는 반면, Arrays.copyOfRange는 사용이 간편하고 타입 안전성이 보장되는 특징이 있습니다.

이 글에서는 두 메서드의 문법, 특징, 성능 비교, 사용 예제, 그리고 적합한 사용 시나리오까지 상세히 분석합니다. 어떤 상황에서 어떤 메서드를 사용하는 것이 더 효율적인지 명확하게 이해하실 수 있을 것입니다.

목차
1. System.arraycopy 개요 및 특징
2. Arrays.copyOfRange 개요 및 특징
3. 두 메서드의 핵심 차이점 비교
4. 성능 분석 및 벤치마크
5. 자주 묻는 질문 (FAQ)

#1. System.arraycopy 개요 및 특징
System.arraycopy는 Java의 네이티브 메서드로, C언어로 구현되어 있어 매우 빠른 성능을 제공합니다. JDK 초기 버전부터 존재해온 가장 기본적인 배열 복사 메서드입니다.
1) 기본 문법
public static native void arraycopy(
    Object src,    // 원본 배열
    int srcPos,    // 원본 배열 시작 위치
    Object dest,    // 대상 배열
    int destPos,    // 대상 배열 시작 위치
    int length    // 복사할 요소 개수
);
. . . . .
2) 파라미터 설명
파라미터 설명
src 원본 배열 (복사할 데이터가 있는 배열)
srcPos 원본 배열에서 복사를 시작할 인덱스 위치
dest 대상 배열 (데이터가 복사될 배열, 미리 생성되어 있어야 함)
destPos 대상 배열에서 데이터가 저장될 시작 인덱스 위치
length 복사할 요소의 개수
. . . . .
3) 주요 특징
(1) 네이티브 메서드
JNI(Java Native Interface)를 통해 시스템 수준에서 최적화된 복사 연산을 수행합니다. C언어로 작성된 네이티브 코드로 실행되기 때문에 Java 메서드보다 훨씬 빠른 성능을 제공합니다.
(2) 반환값 없음
void 타입 메서드로, 별도의 배열을 반환하지 않고 대상 배열(dest)을 직접 수정합니다. 따라서 대상 배열은 메서드 호출 전에 미리 생성되어 있어야 합니다.
(3) 수동 메모리 관리
대상 배열은 미리 생성되어 있어야 하며, 크기가 충분해야 합니다. 크기가 부족하면 ArrayIndexOutOfBoundsException이 발생합니다.
(4) 얕은 복사(Shallow Copy)
객체 배열의 경우 참조값만 복사됩니다. 원본 배열과 복사본 배열의 요소들이 같은 객체를 가리키게 됩니다.
(5) 타입 체크 없음
컴파일 타임에 타입 체크가 이루어지지 않아 런타임 오류 가능성이 있습니다. 호환되지 않는 타입 간 복사 시 ArrayStoreException이 발생합니다.
. . . . .
4) 사용 예제
// 기본 사용법
int[] source = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(source, 0, dest, 0, source.length);
System.out.println(Arrays.toString(dest));  // 출력: [1, 2, 3, 4, 5]

// 부분 복사 (중간에서 시작)
int[] partialDest = new int[3];
System.arraycopy(source, 1, partialDest, 0, 3);
System.out.println(Arrays.toString(partialDest));  // 출력: [2, 3, 4]

// 동일 배열 내에서 이동 (오버랩 처리 가능)
int[] overlap = {1, 2, 3, 4, 5};
System.arraycopy(overlap, 0, overlap, 1, 4);
System.out.println(Arrays.toString(overlap));  // 출력: [1, 1, 2, 3, 4]

#2. Arrays.copyOfRange 개요 및 특징
Arrays.copyOfRangeJava 6부터 도입된 메서드로, java.util.Arrays 클래스에 속해 있습니다. 원본 배열의 특정 범위를 복사하여 새 배열을 생성하는 편리한 방법을 제공합니다.
1) 기본 문법
public static <T> T[] copyOfRange(
    T[] original,    // 원본 배열
    int from,    // 시작 인덱스 (포함)
    int to    // 끝 인덱스 (미포함)
);
. . . . .
2) 파라미터 설명
파라미터 설명
original 원본 배열 (복사할 데이터가 있는 배열)
from 복사를 시작할 인덱스 (이 위치의 요소는 포함됨)
to 복사를 끝낼 인덱스 (이 위치의 요소는 포함되지 않음)
주의: to 인덱스는 미포함입니다. 즉, copyOfRange(arr, 0, 3)은 인덱스 0, 1, 2의 요소만 복사합니다.
. . . . .
3) 주요 특징
(1) 새 배열 반환
복사된 요소들을 포함하는 새로운 배열을 생성하여 반환합니다. 원본 배열은 변경되지 않으며, 독립적인 새 배열이 만들어집니다.
(2) 자동 메모리 할당
반환 배열의 크기는 자동으로 to - from으로 설정됩니다. 개발자가 직접 배열 크기를 계산하거나 생성할 필요가 없어 편리합니다.
(3) 범위 지정 복사
시작 인덱스(from)와 끝 인덱스(to)를 지정하여 부분 복사가 직관적으로 가능합니다. 배열의 특정 구간만 추출할 때 매우 유용합니다.
(4) 타입 안전성
제네릭을 사용하여 컴파일 타임에 타입 체크가 이루어집니다. 타입 불일치로 인한 런타임 오류를 사전에 방지할 수 있습니다.
(5) 내부적으로 System.arraycopy 사용
Arrays.copyOfRange는 내부적으로 System.arraycopy를 호출하여 실제 복사 작업을 수행합니다. 따라서 추가 메서드 호출 오버헤드가 발생합니다.
. . . . .
4) 사용 예제
// 기본 사용법
int[] original = {1, 2, 3, 4, 5};
int[] copy = Arrays.copyOfRange(original, 0, original.length);
System.out.println(Arrays.toString(copy));  // 출력: [1, 2, 3, 4, 5]

// 부분 범위 복사
int[] partialCopy = Arrays.copyOfRange(original, 1, 4);
System.out.println(Arrays.toString(partialCopy));  // 출력: [2, 3, 4]

// 원본보다 큰 범위 지정 (남은 부분은 기본값으로 채워짐)
int[] extendedCopy = Arrays.copyOfRange(original, 2, 7);
System.out.println(Arrays.toString(extendedCopy));  // 출력: [3, 4, 5, 0, 0]

#3. 두 메서드의 핵심 차이점 비교
System.arraycopy와 Arrays.copyOfRange의 차이점을 표로 정리하면 다음과 같습니다.
1) 전체 비교표
비교 항목 System.arraycopy Arrays.copyOfRange
반환 값 void (반환 없음) 새로운 배열 반환
메모리 할당 수동 (대상 배열 필요) 자동 (새 배열 생성)
사용 편의성 다소 복잡함 간단하고 직관적
성능 매우 빠름 (네이티브) 상대적으로 느림
도입 시기 JDK 1.0 Java 6 (JDK 1.6)
구현 방식 네이티브 메서드 (C) System.arraycopy 기반
타입 안전성 낮음 (런타임 체크) 높음 (컴파일 타임 체크)
범위 지정 시작 위치와 길이 시작 인덱스와 끝 인덱스
. . . . .
2) 주요 차이점 상세 설명
(1) 반환 방식의 차이
System.arraycopy는 void 타입으로 반환값이 없으며, 대상 배열을 직접 수정합니다. 반면 Arrays.copyOfRange는 새로운 배열 객체를 생성하여 반환하므로 원본 데이터가 보존됩니다.
// System.arraycopy: 대상 배열 직접 수정
int[] dest = new int[3];
System.arraycopy(source, 0, dest, 0, 3);  // dest 배열이 수정됨

// Arrays.copyOfRange: 새 배열 반환
int[] newArray = Arrays.copyOfRange(source, 0, 3);  // 새 배열 생성
(2) 메모리 관리 차이
System.arraycopy는 개발자가 대상 배열을 미리 생성해야 하므로 메모리를 수동으로 관리해야 합니다. Arrays.copyOfRange는 필요한 크기의 배열을 자동으로 생성하므로 편리하지만, 메모리 할당 비용이 추가됩니다.
(3) 성능 차이
System.arraycopy는 네이티브 메서드로 약 1.5~2배 빠른 성능을 보입니다. Arrays.copyOfRange는 내부적으로 System.arraycopy를 호출하지만, 추가 검증 및 배열 생성 작업으로 인한 오버헤드가 발생합니다.
(4) 타입 안전성 차이
System.arraycopy는 Object 타입 파라미터를 받아 컴파일 타임에 타입 체크가 없습니다. Arrays.copyOfRange는 제네릭을 사용하여 컴파일 타임에 타입 안전성이 보장됩니다.

#4. 성능 분석 및 벤치마크
두 메서드의 성능 차이를 이해하기 위해 벤치마크 테스트를 실행해보겠습니다.
1) 벤치마크 코드
import java.util.Arrays;

public class ArrayCopyBenchmark {
    private static final int ITERATIONS = 10_000_000;
    private static final int ARRAY_SIZE = 100;

    public static void main(String[] args) {
        int[] original = new int[ARRAY_SIZE];
        for (int i = 0; i < ARRAY_SIZE; i++) {
            original[i] = i;
        }

        // System.arraycopy 벤치마크
        long startTime = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            int[] copy = new int[50];
            System.arraycopy(original, 25, copy, 0, 50);
        }
        long systemArrayCopyTime = System.nanoTime() - startTime;

        // Arrays.copyOfRange 벤치마크
        startTime = System.nanoTime();
        for (int i = 0; i < ITERATIONS; i++) {
            int[] copy = Arrays.copyOfRange(original, 25, 75);
        }
        long arraysCopyOfRangeTime = System.nanoTime() - startTime;

        System.out.println("System.arraycopy: " + systemArrayCopyTime / 1_000_000 + " ms");
        System.out.println("Arrays.copyOfRange: " + arraysCopyOfRangeTime / 1_000_000 + " ms");
        System.out.println("성능 차이: " + (double) arraysCopyOfRangeTime / systemArrayCopyTime + "배");
    }
}
. . . . .
2) 벤치마크 결과 분석
실행 환경에 따라 결과는 다를 수 있지만, 일반적으로 System.arraycopy가 Arrays.copyOfRange보다 약 1.5배에서 2배 정도 빠른 성능을 보여줍니다.
메서드 평균 실행 시간 상대 성능
System.arraycopy 약 100ms 기준 (1.0배)
Arrays.copyOfRange 약 150-200ms 1.5-2.0배 느림
. . . . .
3) 성능 차이 발생 이유
(1) 추가 메서드 호출 오버헤드
Arrays.copyOfRange는 내부적으로 System.arraycopy를 호출하지만, 추가 검증 및 설정 작업을 수행합니다. 이로 인해 메서드 호출 스택이 깊어지고 오버헤드가 발생합니다.
(2) 새 배열 할당 비용
Arrays.copyOfRange는 항상 새 배열을 생성하므로 메모리 할당 비용이 발생합니다. System.arraycopy는 이미 생성된 배열을 사용하므로 이 비용이 없습니다.
(3) 범위 검사
Arrays.copyOfRange는 지정된 범위의 유효성을 검사하는 추가 작업을 수행합니다. from이 to보다 크거나, 음수 인덱스인지 등을 체크합니다.
. . . . .
4) 적합한 사용 시나리오
(1) System.arraycopy가 적합한 경우
극도의 성능이 필요할 때 - 대량의 데이터를 빠르게 처리해야 하는 경우
메모리 효율성이 중요할 때 - 새 배열 생성을 피하고 싶을 때
대용량 배열 처리 - 수백만 개 이상의 요소를 복사할 때
이미 존재하는 배열에 데이터 채우기 - 대상 배열이 이미 생성되어 있을 때
배열 내 요소 이동 - 동일 배열 내에서 요소를 이동할 때
(2) Arrays.copyOfRange가 적합한 경우
코드 가독성과 간결함 중시 - 더 간단하고 명확한 코드를 작성하고 싶을 때
부분 배열 추출 - 원본 배열의 특정 범위만 필요할 때
새 배열 반환이 필요할 때 - 원본 데이터를 유지하면서 복사본이 필요할 때
타입 안전성이 중요할 때 - 컴파일 타임에 타입 체크가 필요할 때
확장된 범위 복사 - 원본보다 더 큰 배열이 필요할 때

#5. 자주 묻는 질문 (FAQ)
1) Q: System.arraycopy와 Arrays.copyOfRange 중 어떤 것을 사용해야 하나요?
A: 성능이 매우 중요하고 대량의 데이터를 처리하는 경우에는 System.arraycopy를 사용하는 것이 좋습니다. 반면 코드 가독성과 유지보수성이 중요하고, 일반적인 수준의 데이터를 처리하는 경우에는 Arrays.copyOfRange가 더 나은 선택입니다. 대부분의 일반적인 사용 사례에서는 Arrays.copyOfRange의 편리함과 타입 안전성이 약간의 성능 차이보다 더 큰 가치를 제공합니다.
. . . . .
2) Q: System.arraycopy에서 대상 배열 크기가 부족하면 어떻게 되나요?
A: 대상 배열의 크기가 복사할 데이터를 담기에 부족하면 ArrayIndexOutOfBoundsException이 발생합니다. 예를 들어 destPos + length가 dest.length보다 크면 예외가 발생합니다. 따라서 System.arraycopy를 사용할 때는 반드시 대상 배열의 크기가 충분한지 확인해야 합니다. 복사할 데이터 크기를 정확히 계산하여 배열을 생성하거나, 여유 공간을 두고 큰 크기로 배열을 만드는 것이 안전합니다.
. . . . .
3) Q: Arrays.copyOfRange에서 to 인덱스가 배열 크기보다 크면 어떻게 되나요?
A: Arrays.copyOfRange는 to 인덱스가 원본 배열 크기보다 큰 경우에도 예외를 발생시키지 않고 정상 동작합니다. 원본 배열의 범위를 초과한 부분은 해당 타입의 기본값(int는 0, 객체는 null)으로 채워집니다. 예를 들어 크기가 5인 배열에서 copyOfRange(arr, 2, 8)을 호출하면, 인덱스 2~4의 값은 복사되고 나머지 3개는 기본값으로 채워진 크기 6의 배열이 반환됩니다.
. . . . .
4) Q: 동일한 배열 내에서 요소를 이동할 때 어떤 메서드를 사용해야 하나요?
A: 동일한 배열 내에서 요소를 이동할 때는 System.arraycopy를 반드시 사용해야 합니다. System.arraycopy는 원본과 대상이 같은 배열이어도 오버랩(겹침)을 올바르게 처리합니다. Arrays.copyOfRange는 항상 새 배열을 생성하므로 이러한 용도로는 적합하지 않습니다. 예를 들어 배열의 앞부분을 뒤로 밀어내거나, 뒷부분을 앞으로 당기는 작업에 System.arraycopy를 사용하면 됩니다.
. . . . .
5) Q: 객체 배열을 복사할 때 깊은 복사가 되나요?
A: 아닙니다. 두 메서드 모두 얕은 복사(Shallow Copy)를 수행합니다. 즉, 객체 배열의 경우 객체 자체가 아닌 참조값(주소)만 복사됩니다. 따라서 원본 배열과 복사본 배열의 요소들이 같은 객체를 가리키게 되며, 한쪽에서 객체의 내용을 변경하면 다른 쪽에도 영향을 미칩니다. 깊은 복사가 필요한 경우에는 각 객체를 개별적으로 복제(clone)하거나, 복사 생성자를 사용해야 합니다.
. . . . .
6) Q: 성능 차이가 1.5~2배라면 실제로 체감이 되나요?
A: 배열 크기와 복사 빈도에 따라 다릅니다. 작은 배열(수십~수백 개 요소)을 가끔 복사하는 경우에는 성능 차이가 거의 체감되지 않습니다. 그러나 대용량 배열(수만~수백만 개 요소)을 빈번하게 복사하는 경우에는 성능 차이가 명확히 드러납니다. 예를 들어 게임 엔진의 그래픽 버퍼, 대용량 데이터 처리, 실시간 스트리밍 등에서는 System.arraycopy의 성능 이점이 중요합니다. 일반적인 웹 애플리케이션이나 비즈니스 로직에서는 Arrays.copyOfRange의 편리함이 더 가치 있습니다.
. . . . .
7) Q: System.arraycopy에서 null 배열을 전달하면 어떻게 되나요?
A: 원본 배열(src)이나 대상 배열(dest) 중 하나라도 null이면 NullPointerException이 발생합니다. System.arraycopy는 null 체크를 자동으로 하지 않으므로, 개발자가 직접 null 여부를 확인해야 합니다. Arrays.copyOfRange도 마찬가지로 원본 배열이 null이면 NullPointerException이 발생합니다. 안전한 코드를 작성하려면 배열 복사 전에 null 체크를 수행하는 것이 좋습니다.
. . . . .
8) Q: 다차원 배열도 복사할 수 있나요?
A: 네, 가능합니다. 하지만 1차원 배열처럼 단순하지 않습니다. 2차원 배열(int[][])은 실제로는 배열의 배열이므로, 각 행을 개별적으로 복사해야 합니다. 예를 들어 2차원 배열 전체를 복사하려면 외부 배열을 먼저 복사하고, 각 행(내부 배열)을 반복문으로 하나씩 복사해야 합니다. 단순히 외부 배열만 복사하면 얕은 복사가 되어 각 행이 원본과 같은 배열을 가리키게 됩니다.
. . . . .
9) Q: Arrays.copyOfRange의 from과 to가 같으면 어떻게 되나요?
A: from과 to가 같으면 크기가 0인 빈 배열이 반환됩니다. 예를 들어 Arrays.copyOfRange(arr, 3, 3)을 호출하면 요소가 없는 빈 배열이 만들어집니다. 이는 예외가 아니며 정상적인 동작입니다. 반면 from이 to보다 크면 IllegalArgumentException이 발생합니다. 따라서 from ≤ to 조건을 항상 만족해야 합니다.
. . . . .
10) Q: 어떤 경우에 clone() 메서드 대신 이 메서드들을 사용하나요?
A: 배열 전체를 복사하는 경우에는 clone() 메서드가 가장 간단합니다(int[] copy = original.clone()). 그러나 배열의 일부만 복사하거나, 특정 위치에 데이터를 삽입하는 경우에는 System.arraycopy나 Arrays.copyOfRange를 사용해야 합니다. clone()은 배열 전체를 복사하는 것만 가능하며, 부분 복사나 특정 위치로의 복사는 지원하지 않습니다. 또한 System.arraycopy는 성능이 더 우수하므로, 대용량 배열을 복사할 때는 clone()보다 System.arraycopy가 더 나은 선택일 수 있습니다.

마무리
이 글에서는 Java의 두 가지 배열 복사 메서드인 System.arraycopy와 Arrays.copyOfRange의 차이점을 상세히 비교 분석했습니다.
핵심 내용을 정리하면 다음과 같습니다.
System.arraycopy: 네이티브 메서드로 최고의 성능 제공, 대용량 데이터 처리에 적합, 수동 메모리 관리 필요
Arrays.copyOfRange: 사용이 간편하고 직관적, 타입 안전성 보장, 일반적인 용도에 적합
성능 차이: System.arraycopy가 약 1.5~2배 빠르지만, 일반적인 경우 체감 어려움
선택 기준: 극한의 성능이 필요하면 System.arraycopy, 코드 가독성이 중요하면 Arrays.copyOfRange
실제 프로젝트에서는 두 메서드의 특성을 이해하고 상황에 맞게 선택하는 것이 중요합니다. 극도의 성능이 필요한 경우가 아니라면, 대부분의 일반적인 사용 사례에서는 코드 가독성과 유지보수성 측면에서 Arrays.copyOfRange가 더 좋은 선택일 수 있습니다.
게임 엔진, 실시간 데이터 처리, 대용량 스트리밍 등 성능이 매우 중요한 분야에서는 System.arraycopy를 사용하고, 일반적인 비즈니스 로직이나 웹 애플리케이션에서는 Arrays.copyOfRange를 사용하는 것을 권장합니다.
Java 배열 복사에 관심이 있는 분들은 이 글의 내용을 기반으로 실제 프로젝트에 적용해보시기 바랍니다.
긴 글 읽어주셔서 감사합니다.

끝.
반응형