System.arraycopy vs Arrays.copyOfRange 차이점 완벽 비교
안녕하세요.
이번 포스팅에서는 Java에서 배열을 복사할 때 자주 사용하는 두 메서드 System.arraycopy
와 Arrays.copyOfRange
의 차이점에 대해 자세히 알아보겠습니다. 두 메서드는 얼핏 보기에 비슷한 기능을 수행하지만, 내부 구현과 사용 방법, 그리고 성능 측면에서 중요한 차이가 있습니다. 이 글을 통해 어떤 상황에서 어떤 메서드를 사용하는 것이 더 효율적인지 명확하게 이해하실 수 있을 것입니다.

목차
#1. System.arraycopy 개요 및 특징
System.arraycopy
는 Java의 네이티브 메서드로, C언어로 구현되어 있어 매우 빠른 성능을 제공합니다. JDK 초기 버전부터 존재해온 가장 기본적인 배열 복사 메서드입니다.
기본 문법
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); |
파라미터 설명
src
: 원본 배열srcPos
: 원본 배열에서 복사를 시작할 위치dest
: 복사본 배열 (복사될 대상)destPos
: 복사본 배열에서 데이터가 저장될 시작 위치length
: 복사할 요소의 개수
특징
- 네이티브 메서드: JNI(Java Native Interface)를 통해 시스템 수준에서 최적화된 복사 연산 수행
- 반환값 없음: void 타입 메서드로, 별도의 배열을 반환하지 않고 대상 배열을 직접 수정
- 수동 메모리 관리: 대상 배열은 미리 생성되어 있어야 하며, 크기가 충분해야 함
- 얕은 복사(Shallow Copy): 객체 배열의 경우 참조값만 복사
- 타입 체크 없음: 컴파일 타임에 타입 체크가 이루어지지 않음 (런타임 오류 가능성)
#2. Arrays.copyOfRange 개요 및 특징
Arrays.copyOfRange
는 Java 6부터 도입된 메서드로, java.util.Arrays
클래스에 속해 있습니다. 원본 배열의 특정 범위를 복사하여 새 배열을 생성하는 편리한 방법을 제공합니다.
기본 문법
public static <T> T[] copyOfRange(T[] original, int from, int to); |
파라미터 설명
original
: 원본 배열from
: 복사를 시작할 인덱스 (포함)to
: 복사를 끝낼 인덱스 (미포함)
특징
- 새 배열 반환: 복사된 요소들을 포함하는 새로운 배열을 생성하여 반환
- 자동 메모리 할당: 반환 배열의 크기는 자동으로
to - from
으로 설정됨 - 범위 지정 복사: 시작 인덱스와 끝 인덱스를 지정하여 부분 복사 가능
- 타입 안전성: 제네릭을 사용하여 컴파일 타임에 타입 체크
- 내부적으로 System.arraycopy 사용:
No | 특성 | System.arraycopy | Arrays.copyOfRange |
1 | 반환 값 | void (반환 없음) | 새로운 배열 |
2 | 메모리 할당 | 수동 (대상 배열 필요) | 자동 (새 배열 생성) |
3 | 사용 편의성 | 다소 복잡함 | 간단하고 직관적 |
4 | 성능 | 매우 빠름 | 상대적으로 느림 (오버헤드 존재) |
5 | 도입 시기 | JDK 1.0 | Java 6 (JDK 1.6) |
6 | 구현 방식 | 네이티브 메서드 | System.arraycopy 기반 Java 코드 |
7 | 타입 안전성 | 낮음 (런타임 체크) | 높음 (컴파일 타임 체크) |
8 | 범위 지정 | 시작 위치와 길이로 지정 | 시작 인덱스와 끝 인덱스로 지정 |
#4. 성능 분석
두 메서드의 성능 차이를 이해하기 위해 간단한 벤치마크 테스트를 실행해보겠습니다.
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); } } |
벤치마크 결과
실행 환경에 따라 결과는 다를 수 있지만, 일반적으로 System.arraycopy
가 Arrays.copyOfRange
보다 약 1.5배에서 2배 정도 빠른 성능을 보여줍니다. 이러한 성능 차이는 다음과 같은 이유 때문입니다:
- 추가 메서드 호출 오버헤드:
Arrays.copyOfRange
는 내부적으로System.arraycopy
를 호출하지만, 추가 검증 및 설정 작업을 수행합니다. - 새 배열 할당 비용:
Arrays.copyOfRange
는 항상 새 배열을 생성하므로 메모리 할당 비용이 발생합니다. - 범위 검사:
Arrays.copyOfRange
는 지정된 범위의 유효성을 검사하는 추가 작업을 수행합니다.
#5. 사용 예제
System.arraycopy 사용 예제
// 기본 사용법 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] // 동일 배열 내에서 이동 (오버랩 처리 가능) System.arraycopy(source, 0, source, 1, 4); System.out.println(Arrays.toString(source)); // 출력: [1, 1, 2, 3, 4] |
Arrays.copyOfRange 사용 예제
// 기본 사용법 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] |
#6. 적합한 사용 시나리오
System.arraycopy가 적합한 경우
- 극도의 성능이 필요할 때: 성능이 매우 중요한 경우
- 메모리 효율성이 중요할 때: 새 배열 생성을 피하고 싶을 때
- 대용량 배열 처리: 대량의 데이터를 효율적으로 복사할 때
- 이미 존재하는 배열에 데이터 채우기: 대상 배열이 이미 생성되어 있을 때
- 배열 내 요소 이동: 동일 배열 내에서 요소를 이동할 때(오버랩 처리 가능)
Arrays.copyOfRange가 적합한 경우
- 코드 가독성과 간결함 중시: 더 간단하고 명확한 코드를 작성하고 싶을 때
- 부분 배열 추출: 원본 배열의 특정 범위만 필요할 때
- 새 배열 반환이 필요할 때: 원본 데이터를 유지하면서 복사본이 필요할 때
- 타입 안전성이 중요할 때: 컴파일 타임에 타입 체크가 필요할 때
- 확장된 범위 복사: 원본보다 더 큰 배열이 필요할 때 (남은 부분은 기본값으로 채워짐)
#7. 주의사항
System.arraycopy 사용 시 주의점
- 배열 범위 검증 없음: 잘못된 인덱스 범위를 지정하면
ArrayIndexOutOfBoundsException
발생 - 대상 배열 크기 확인: 대상 배열의 크기가 충분히 커야 함
- 타입 호환성: 호환되지 않는 타입의 배열 간 복사 시
ArrayStoreException
발생 - null 처리 없음: 배열이 null이면
NullPointerException
발생
Arrays.copyOfRange 사용 시 주의점
- 성능 오버헤드: 추가 메서드 호출과 새 배열 생성으로 인한 성능 저하
- 메모리 사용량 증가: 항상 새 배열을 생성하므로 메모리 사용량 증가
- 음수 인덱스 불가: 음수 인덱스를 지정하면
ArrayIndexOutOfBoundsException
발생 - from이 to보다 큰 경우:
IllegalArgumentException
발생
#8. 결론
System.arraycopy
와 Arrays.copyOfRange
는 각각 고유한 장단점을 가지고 있습니다:
- System.arraycopy는 네이티브 구현으로 최고의 성능을 제공하지만, 사용법이 다소 복잡하고 수동 메모리 관리가 필요합니다. 성능이 중요하거나 대량의 데이터를 처리할 때 이상적입니다.
- Arrays.copyOfRange는 사용이 간편하고 직관적이며, 타입 안전성이 보장되지만, 항상 새 배열을 생성하기 때문에 성능 오버헤드가 있습니다. 가독성이 중요하거나 원본 배열의 특정 부분만 필요할 때 좋은 선택입니다.
실제 프로젝트에서는 두 메서드의 특성을 이해하고 상황에 맞게 선택하는 것이 중요합니다. 극도의 성능이 필요한 경우가 아니라면, 대부분의 일반적인 사용 사례에서는 코드 가독성과 유지보수성 측면에서 Arrays.copyOfRange
가 더 좋은 선택일 수 있습니다.
여러분의 프로젝트에서는 어떤 배열 복사 메서드를 주로 사용하시나요? 댓글로 여러분의 경험을 공유해주세요!
마무리
긴 글 읽어주셔서 감사합니다.
끝.
참고 자료
'■Development■ > 《Java》' 카테고리의 다른 글
[Java] throw와 throws의 차이 (0) | 2024.07.24 |
---|---|
[Java] Java Class 파일 DeCompile (0) | 2020.04.08 |
[Java] StringBuffer vs StringBuilder (0) | 2019.09.26 |
[Java] Handler 완벽 가이드 (0) | 2019.09.05 |
[Java] System.arraycopy 완벽 가이드 (0) | 2019.09.04 |