리스트를 초기화 할 때, 모두 같은 값으로 초기화 해야 하는 경우가 많습니다. 예를 들어 60개의 원소를 모두 0으로 초기화 하거나, 혹은 -1로 초기화 하는 것이 이에 속합니다. c++에서는 vector의 resize를 이용하면 매우 손쉽게 할 수 있었는데, 자바는 아니였습니다. 간단하게 하는 방법 중 하나는, 콜렉션의 nCopies의 힘을 빌리면 됩니다.

 

 위 그림을 보시면, Collections.nCopies 메서드를 썼음을 알 수 있는데요. 1번째 인자인 n은 갯수를 의미합니다. 예를 들어 60개의 원소를 0으로 초기화 하고 싶다면 1번째 인자에는 60을 넣으면 됩니다. 2번째 인자에는 당연하게도, 0을 넣어주시면 됩니다.

 

 

 당연한 이야기일지도 모르겠지만, Boxing 객체가 아닌 다른 객체를 nCopies로 채울 때는 조심하셔야 합니다. 눈치를 채셨을 지도 모르겠지만, 이는 얕은 복사 때문입니다. 보통 저는 Integer, Long과 같은 Boxing이 된 객체로 초기화 할 때 이용합니다. 해당 메서드가 어떻게 동작하는지 보도록 하겠습니다.

 


 먼저, nCopies라는 것을 호출하면, 내부적으로 새로운 CopiesList를 호출하게 됩니다.

 

 4987번째 줄로 들어가 봅시다.

 

 

 Collections 안에 있는 내부 객체인 모양이군요. 이것은 List를 구현한 것인데요. 필드가 이상한 것들로 이루어져 있습니다. element와 n으로만 이루어져 있습니다.

 

 이는 디버그로 확인해 보아도 마찬가지입니다. 보통, List라고 하면, 이런 그림을 떠올리기 쉽습니다. 예를 들어 size가 5라면, 원소들을 저장해 놓는 공간은 배열 형태로 이루어져 있는 무언가.

 

 

 대략 이런 식입니다. 그런데, 배열의 길이는 사실상 capacity 역할을 하고, 동적 배열의 특성상 capacity와 size는 다를 것이니, size도 같이 있을 겁니다.

 

 size가 60인데도, element가 가리키는 것은 배열이 아닌 Integer Object 0일 뿐입니다. 그런데, 이게 AbstractList를 상속하고, 랜덤 억세스와 Serializable를 구현한다니. 신기할 따름입니다. 사실 저것은 그냥 단순히 추상적일 뿐, 같은 원소가 있는 List는 어떤 원소가 있는지와 size만 알면, 리스트를 구축하는 데에는 크게 문제가 없습니다. CopiesList는 remove나 add가 없는 게 이상하지 않으신가요? 이는, 구현할 필요가 없었기 때문이 아닐까 싶습니다. 단지, CopiestList는 원소 k가 n개가 있다는 정보가 중요하기 때문입니다.

 


 디버거를 계속 돌려 봅시다. 그러면, 어느 순간, 0이 60개 초기화가 된 모양이 나오는데요. CopiesList는 단순히 원소와, 몇 개로 이루어져 있는가만 나타나 있습니다. 따라서, ArrayList의 생성자 안에서 무언의 작업이 이루어 졌으리라 추정해 볼 수 있습니다.

 

 어느 과정에서 이런 일이 일어났을까요?

 

 

 생성자를 호출하자 마자, c.toArray()를 호출해 버립니다. 이 안에서 무슨 일이 일어날까요?

 

 Collections의 5032번째 줄을 봅시다.

 

 그러면, 다른 것은 없고, [0, n)까지 element로 채워 버리게 됩니다.

 

 요렇게 그림이 그려집니다. 즉, 얕은 복사가 되어 버립니다. 사실 저는 Integer로 초기화를 해서 별 문제가 되지는 않았습니다만, List 객체나, 아니면 Dog 객체 등으로 정확히 n개 만큼 초기화 해야 할 경우에는 이 점이 문제가 될 수도 있어요. 이럴 때는 어떻게 해야 할까요?

 

 

 

 간단합니다. for loop를 n번 돌면서 add를 n번 하면 됩니다. 새로 생성된 객체를 add를 하게 되면, 기존에 생성된 객체와는 별개의 객체를 넣습니다. 따라서, 얕은 복사의 문제점은 없어지게 됩니다. 

 

 결과를 확인해 보면, 얕은 복사가 되지 않았음을 알 수 있습니다.