[Kotlin][deep copy] 리스트의 마지막 원소가 사라져 버렸다.

Kotlin 코드에서 deep copy 를 하지 않은 채 원본을 clear 하니까 리스트의 마지막 원소가 사라지는 문제가 발생했다.

리스트의 마지막 원소만 사라진다.

val testList: MutableList<Int>
val listOfTestList: MutableList<List<Int>>

.forEach 블록 내에서 testList 를 몇 개 생성하면서 listOfTestList 에 추가하였다:

listOfTestList.add(testList)

나중에 listOfTestList 를 출력해 보니 감쪽같이 마지막 원소만 사라졌다. 마지막 element 가 [] 이렇게 빈 채로 나오고 그 앞의 모든 element 는 제대로 출력되었다. 즉, 만약 element 가 10개여야 맞는 상황이면 처음 9개는 제대로 출력되고 마지막 element 만 [] 로 출력되는 식이다.

어제저녁 이 문제를 처음 발견하였다. 나중에 안 일이지만 이미 여러 날 전부터 있던 문제였다. 그동안 눈치를 못 채고 있었다. 문제를 해결하기 위해서 부단히 노력하였다. 새벽 1시가 넘도록 성공하지 못했다.

혹시 .clear() 때문?

자고 일어나서 밥을 먹고 다시 문제 해결에 매진했다. 어젯밤에는 리스트 생성 함수에 문제가 있나 싶어서 함수 코드를 집중 분석했지만 성과가 없었다. 뭐라도 해봐야겠기에 여기저기 조금씩 고쳐서 실행해 보고 효과가 없으면 Ctrl+Z 를 눌러서 원래의 코드로 되돌아가기를 반복했다. 그러다가 리스트 생성/복사 부분에 집중하여 살펴보았다.

잘 살펴보니 .forEach 블록이 끝난 다음에 이런 게 보인다:

testList.clear()

여기에 들어 있던 값은 이미 다른 더 큰 리스트에 넣었으니까 필요없을 것 같아서 당연하다고 생각하면서 값을 없앴다. 눈꼽만큼이라도 메모리 확보하려는 마음도 있었다.

문제가 해결되지 않으니까 지푸라기라도 잡는 심정으로 .clear() 실행 부분을 주석 처리해 보았다:

//testList.clear()

오호~ 해결되었다!!

deep copy

이대로 넘아가도 코드는 잘 실행된다. 그러나 이 문제를 그냥 떠나보내기는 어딘가 찜찜한데? 그래, 근본 원인을 찾아야겠다. .clear() 실행 부분을 없애지 않고도 문제가 생기지 않게 해야겠다. 그러다가 deep copy 문제가 언뜻 떠올랐다. 혹시 이것도 deep copy 문제일지도 모르겠는데? 그럼, 얼마 전에 익힌 .toMutableList() 신공을 써 보아야겠군. 리스트를 복사할 때 다음과 같이 복사하면 deep copy 가 되어 원본 리스트가 바뀌어도 복사본은 안 바뀐다:

bbb = aaa.toMutableList()

이걸 응용해서 리스트 추가하는 부분을 다음과 같이 바꾸었다:

blahBlah.forEach {

// testList 를 생성하는 코드가 여기에 있음.
listOfTestList.add(testList.toMutableList())

}

그런 다음에 .clear() 실행부를 되살렸다.

testList.clear()

앱을 실행해 보니 리스트의 마지막 원소도 사라지지 않고 결과가 잘 출력되었다. 이렇게 해서 딴은 바람직한 방식으로 문제가 해결되었다.

등 떠밀려서 deep copy 되는 시점

이 경험을 통해서 코틀린의 작동 방식의 일면을 엿볼 수 있었다.

listOfTestList 에 이미 추가됐던 값들은 testList 에 새 값이 할당되기 직전에 자동으로 deep copy 된다. deep copy 를 즉시 실행하고 싶으면 위에서 내가 한 것처럼 .toMutableList() 를 붙여서 값을 추가하면 된다:

“코틀린, 너 나중에 떠밀려서 마지못해 deep copy 하지 말고 지금 당장 deep copy 해라.”

Leave a Comment