-- System.arraycopy()
if (high + 1 - low >= 0)
System.arraycopy(mergedArr, low, arr, low, high + 1 - low);
-- 루프
for (int idx = low; idx <= high; idx++)
arr[idx] = mergedArr[idx];
동일한 동작을 수행하지만, 방식이 다름.
System.arraycopy()는 네이티브 최적화가 적용된 고속 메모리 복사.
for 루프는 자바 코드로 반복문을 통해 하나씩 복사.
System.arraycopy()는 네이티브 최적화가 적용된 고속 메모리 복사.
JNI(Java Native Interface) 기반으로 네이티브 코드에서 실행되며, 성능 최적화가 되어 있음
Java의 System.arraycopy()는 내부적으로 C 또는 어셈블리 수준에서 최적화된 메모리 복사 연산을 수행하기 때문에 for 루프보다 훨씬 빠름
내부적으로 JVM의 Unsafe.copyMemory()와 유사한 방식으로 동작
두 코드의 동작은 동일하지만, System.arraycopy()가 JNI 기반으로 더 최적화된 방식임.
arraycopy와 for 루프 모두 얕은 복사(Shallow Copy)
객체의 참조(메모리 주소)만 복사하는 방식.
복사된 객체와 원본 객체가 같은 메모리 주소를 공유.
하나의 객체를 수정하면 원본에도 영향을 미침.
System.arraycopy(), Arrays.copyOf(), clone() 등으로 수행 가능.
객체 자체를 새로운 메모리 공간에 완전히 복사하는 방식.
복사된 객체와 원본 객체가 독립적인 메모리 주소를 가짐
복사된 객체를 수정해도 원본에는 영향을 주지 않음.
직접 새 객체를 생성하여 필드를 개별적으로 복사하거나, clone()을 오버라이딩하여 수행.
정수형 배열을 복사할 때 위 두 방법을 사용했다. 단순히 값을 복사하니까 깊은 복사라고 이해했지만 이는 얕은 복사였다.
얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)의 개념은 참조 방식에 따라 달라진다.
int[] arr = {1, 2, 3, 4, 5};
int[] tmp = Arrays.copyOf(arr, arr.length); // 복사
tmp[0] = 100; // tmp 변경
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5] (원본 영향 없음)
System.out.println(Arrays.toString(tmp)); // [100, 2, 3, 4, 5] (복사본만 변경됨)
tmp는 새로운 배열 메모리 공간을 가짐 → arr과 독립적.
이 경우 "얕은 복사"지만 깊은 복사와 동작 차이가 없음 → "값을 복사했기 때문".
tmp를 수정해도 arr에는 영향 없음.
이 경우 "얕은 복사"지만 깊은 복사와 동작이 같아 헷갈릴 수 있음.
String[] arr = {"hello", "world"};
String[] tmp = Arrays.copyOf(arr, arr.length); // 얕은 복사
tmp[0] = "changed";
System.out.println(Arrays.toString(arr)); // ["hello", "world"] (원본 영향 없음)
System.out.println(Arrays.toString(tmp)); // ["changed", "world"] (복사본만 변경됨)
String은 불변 객체(Immutable Object)이므로 동작이 기본형 배열과 비슷해 보임.
하지만, 만약 객체 내부의 속성을 수정할 경우, 원본에도 영향을 줄 수 있음.
class Person {
String name;
Person(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Person[] arr = {new Person("Alice"), new Person("Bob")};
Person[] tmp = Arrays.copyOf(arr, arr.length); // 얕은 복사
tmp[0].name = "Changed"; // tmp의 첫 번째 객체 변경
System.out.println(arr[0].name); // "Changed" (원본도 변경됨!)
System.out.println(tmp[0].name); // "Changed"
}
}
객체 배열에서 Arrays.copyOf() 또는 System.arraycopy()를 사용하면, 객체의 참조만 복사됨(즉, 같은 인스턴스를 가리킴).
tmp[0]의 name을 바꾸면, 원본 arr[0]의 name도 바뀜 → 이것이 얕은 복사의 문제점.
14배의 속도 차이를 보인다.
System.arraycopy() 실행 시간:
175708 ns
public class ArrayCopyBenchmark {
public static void main(String[] args) {
int size = 1000000; // 복사할 배열 크기
int[] source = new int[size];
int[] destination = new int[size];
// 배열 초기화
for (int i = 0; i < size; i++) {
source[i] = i;
}
long start = System.nanoTime();
System.arraycopy(source, 0, destination, 0, size);
long end = System.nanoTime();
System.out.println("System.arraycopy() 실행 시간: " + (end - start) + " ns");
}
}
for 루프 실행 시간:
2480625 ns
public class ForLoopCopyBenchmark {
public static void main(String[] args) {
int size = 1000000; // 복사할 배열 크기
int[] source = new int[size];
int[] destination = new int[size];
// 배열 초기화
for (int i = 0; i < size; i++) {
source[i] = i;
}
long start = System.nanoTime();
for (int i = 0; i < size; i++) {
destination[i] = source[i];
}
long end = System.nanoTime();
System.out.println("for 루프 실행 시간: " + (end - start) + " ns");
}
}