1차원 배열 복사
copyOf() 메소드를 사용하면 deep copy 가 된다.
새로운 레퍼런스 주소로 별도 공간에 array 가 할당된다.
copyOf 로 deep copy 하면
복사된 배열의 값을 수정해도 원본 배열에는 영향을 미치지 않는다.
1차원 배열 복사 Test
// 1차원 배열 copy
@Test
internal fun firstArray() {
val nums = intArrayOf(1, 3, 5, 7, 9)
val copiedArr = nums.copyOf()
// copy 된 배열 값 수정
copiedArr[0] = 0
println("Original Array")
for (num in nums) print("$num ")
println()
println("==================")
println("Copied Array")
for (num in copiedArr) print("$num ")
}
결과
copyOf with New Size
copyOf() 의 인자로 복사본의 크기를 넘길 수 있는데
기존 배열보다 적은 수로 지정하면 짤림이 일어나고 더 크게 설정하면 Falsy (null, 0, false) 등의 값으로 padding 된다.
@Test
internal fun testNewSizeArray() {
val nums = intArrayOf(1, 3, 5, 7, 9)
// if newSize is greater than original one, padding value is to be falsy one (null, false, 0).
println("Over sized array")
val overSizedCopyArr = nums.copyOf(10)
for (num in overSizedCopyArr) print("$num ")
println()
println("Truncated array")
// if newSize is less than original one, copy array is truncated
val truncatedCopyArr = nums.copyOf(3)
for (num in truncatedCopyArr) print("$num ")
// [Result]
// Over-sized array
// 1 3 5 7 9 0 0 0 0 0
// Truncated array
// 1 3 5
}
Clone 메소드를 통한 복사
copyOf 와 마찬가지로 Deep Copy 가 일어나기 때문에 복사본을 수정해도 원본에 영향을 미치지 않는다.
@Test
internal fun testClone() {
val nums = intArrayOf(1, 3, 5, 7, 9)
val clonedArr = nums.clone()
clonedArr[0] = 0
println("======Original Array=====")
for (num in nums) print("$num ")
println()
println("======Copied Array=====")
for (num in clonedArr) print("$num ")
// [Result]
// ======Original Array=====
//1 3 5 7 9
//======Copied Array=====
//0 3 5 7 9
}
copyOfRange()
지정한 범위 내에서 copy할 때 사용한다.
java, kotlin 진영에서의 range 는 보통 [시작 인덱스, 종료 인덱스) 로 종료 인덱스는 작업 대상에 포함시키지 않는다.
@Test
internal fun testCopyOfRange() {
val nums = intArrayOf(1, 3, 5, 7, 9)
// [start index, end index)
val rangeCopiedArr = nums.copyOfRange(1, 3)
rangeCopiedArr.forEach { print("$it ") }
// [Result]
// 3 5
}
⚠️ 2차원 배열 복사 주의
@Test
internal fun testSecondArray() {
val secondArrays = arrayOf(
intArrayOf(1, 3, 5, 7, 9),
intArrayOf(2, 4, 6, 8, 10),
intArrayOf(0, -13, -15, 100, 15)
)
val copiedArray = secondArrays.copyOf()
copiedArray[0][0] = 0
println("Original Array!")
for (arr in secondArrays) {
for (num in arr) print("$num ")
println()
}
println("======================")
println("Copied Array!")
for (arr in copiedArray) {
for (num in arr) print("$num ")
println()
}
}
결과
왜 이런 일이 일어나는 걸까?
2차원 배열을 도식화해보자.
2차원 배열의 원소를 arr[row][col] 로 표현했을 때 경우
배열의 size(행 개수) 만큼, 즉, row 에 대한 Deep Copy 만 일어난다.
행 속의 열(col) 은 Deep Copy 의 대상이 아니다.
배열의 각각의 arr[row] 는 arr[row][col1] arr[row][col2] .... arr[row][last col] 의 시작 주소를 가리킨다.
즉, 1차원 배열의 시작주소를 가리키는 arr[row] 의 값만 새로운 레퍼런스로 Deep Copy 가 일어난다. (JVM 에서는 포인터가 아니라 레퍼런스로 강제되어있다.) 따라서 복사된 레퍼런스로 접근해도 동일한 원본 배열에 접근하게 된다.
copyOf 의 원형
@kotlin.internal.InlineOnly
public actual inline fun <T> Array<T>.copyOf(): Array<T> {
// 2차원 배열이 인자로 들어오면, size 는 행의 개수가 된다.
// 열은 Deep Copy 대상이 아니다.
return java.util.Arrays.copyOf(this, size)
}
2차원 배열 Deep Copy
물론 모든 원소를 순회하며 복사해도 괜찮지만
한 행씩 복사하는 방법이 있다.
@Test
internal fun testDeepCopy2DimensionalArray() {
val secondArrays = arrayOf(
intArrayOf(1, 3, 5, 7, 9),
intArrayOf(2, 4, 6, 8, 10),
intArrayOf(0, -13, -15, 100, 15)
)
// deep copy with for each row
val copiedArr = Array(secondArrays.size){idx-> secondArrays[idx].copyOf()}
copiedArr[0][0] = 0
println("Original Array!")
for (arr in secondArrays) {
for (num in arr) print("$num ")
println()
}
println("==============")
println("Copied Array!")
for (arr in copiedArr) {
for (num in arr) print("$num ")
println()
}
}
결과
'JVM > Kotlin' 카테고리의 다른 글
[Kotlin] Priority Queue (0) | 2022.10.22 |
---|---|
[Kotlin] Array vs ArrayList vs LinkedList vs Queue (0) | 2022.09.18 |
[Kotlin] data class (0) | 2022.09.18 |
[Java, Kotlin] equals, HashCode (0) | 2022.09.18 |
[Kotlin] HashMap, HashSet, LinkedHashSet (0) | 2022.09.17 |