[Java] list - sublist 주의할 점!

iniestar·2022년 1월 11일
1

Java의 List 사용 시 일부분을 잘라내기 할 경우 sublist()를 사용하는 경우가 있다.
그러나 해당 함수의 리턴 값은 list로 반환되나 여기에 엄청난 함정이 숨어져 있다!

sublist()는 다음과 같이 사용할 수 있다.

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.sublist(0,1);

위와 같이 인덱스를 자르는 용도로 사용하지만 여기에는 메모리 누수 위험성이 있다. (http://stackoverflow.com/questions/4179236/does-the-list-sublist-method-prevent-garbage-collection-of-the-rest-of-the-lis)

sublist()를 자세히 들여다 보면 아래와 같은 코드로 되어 있다.

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}

위 코드에서 알 수 있듯이 return new SubList 새롭게 메모리 할당하여 list를 반환하고 있으며, 매개변수로 현재 리스트 객체(this)를 넣어 주고 있다. 문제는 새롭게 생성하는 클래스의 생성자이다.

protected SubList(SubList<E> parent, int fromIndex, int toIndex) {
    this.root = parent.root;
    this.parent = parent;
    this.offset = parent.offset + fromIndex;
    this.size = toIndex - fromIndex;

위 코드는 SubList 클래스를 생성 시 호출되는 생성자 이다. this로 넘겨줬던 리스트의 객체는 parent로 저장되어 관리되는 것을 확인 할 수 있다. 예를 들어, 1만개의 리스트가 있는 객체를 자르기 할 경우 1만개의 리스트가 캐싱된다는 얘기다.

또 다른 문제는 sublist()로 반환받은 리스트는 언뜻 보이기에 원본 리스트에서 인덱싱되어 보이지만 사실상 그렇지 않다. 아래 코드를 실행해 보자.

List<Integer> alist = new ArrayList<>();
alist.add(1);
alist.add(2);
alist.add(3);
List<Integer> blist = alist.sublist(0,1);
blist.remove(0);

Exception in thread "main" java.util.ConcurrentModificationException

	at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)

	at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)

	at java.util.AbstractList.listIterator(AbstractList.java:299)

	at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)
    					...

왜 이런 에러가 날까?
정답은 SubList 클래스의 생성자에 숨어져 있다. 생성자에 있었던 parent의 값이 함께 반환되기 때문에 인덱싱 한 값이더라도 영향을 받는다.

이를 방지하기 위해서는 parent가 초기화 된 새로운 리스트를 생성하는 방법을 사용해야 한다!

생성 방법은 아래와 같다.

List<Integer> subList = new ArrayList<>(alist.subList(0, 3));

여기까지, List의 간편하면서도 무서운 면을 살펴 보았다. 어찌보면 아는 사람만 아는 부분일 수 도 있기 때문에 중요한 부분이라 정리를 해보았다.

누군가 도움이 되었기를...

profile
while(true) { 손가락 관절염++ };

0개의 댓글