영호
[JAVA] Collections.emptyList() vs List.of() 본문
개요
우아한테크코스 체스미션 진행 중 빈 리스트를 반환해야 하는 일이 있었는데, 그동안 빈 리스트를 반환할 때 무심코 사용하던 코드들에 대해 어떤 차이가 있는지 궁금해서 찾아본 과정에 대해 작성해보겠습니다:)
Collections.emptyList()
처음에는 단순히 Collections에서 정적 팩토리 메서드를 이용해 빈 리스트를 생성해 반환해주나? 라는 생각으로 접근했습니다. Collections.emptyList()내부 구현을 봤더니 예상하지 못했던 코드가 있었습니다.
EmptyList라는 클래스가 존재한다.
EmptyList라는 클래스가 별도로 존재했고 이를 List<T>타입으로 캐스팅해 반환해주고 있었습니다.
EmptyList의 Collections의 메서드들에 대해 빈 리스트에 맞게 정의되어 있습니다. get을 하려하면 IndexOutOfBoundsException예외 발생, size()는 return 0; 등이 있습니다.
아래 캡쳐본은 static EmptyList클래스의 구현 코드 중 일부입니다.
Immutable하다.
공식문서를 보면 아래와 같이 나와있습니다.
즉, 수정이 불가능한 리스트가 반환됩니다.
List<Object>가 반환된다.
List<Object> emptyList = Collections.emptyList();
위 코드와 같이 Collections.emptyList()는 List<Object>가 반환됩니다.
List<Object> emptyList = Collections.emptyList();
System.out.println(emptyList.getClass());
emptyList.add("1");
emptyList는 Immutable하기 때문에 add메서드를 호출하면 UnsupportedOperationException이 발생합니다.
EmptyList외 다양한 Empty자료구조
List외에도 Map, Set등 다양한 자료 구조에 대해 EmptyList와 동일하게 구현되어 있습니다.
List.of()
List.of()를 통해서도 리스트를 생성할 수 있습니다. 다만 List.of()는 ImmutableCollections의 리스트가 반환되기 때문에 이 역시 불변합니다.
List인터페이스의 of메서드
static <E> List<E> of() {
return ImmutableCollections.emptyList();
}
ImmutableCollections의 emptyList메서드
static <E> List<E> emptyList() {
return (List<E>) ListN.EMPTY_LIST;
}
세부 구현 코드
static final class ListN<E> extends AbstractImmutableList<E>
implements Serializable {
// EMPTY_LIST may be initialized from the CDS archive.
static @Stable List<?> EMPTY_LIST;
static {
VM.initializeFromArchive(ListN.class);
if (EMPTY_LIST == null) {
EMPTY_LIST = new ListN<>();
}
}
@Stable
private final E[] elements;
@SafeVarargs
ListN(E... input) {
// copy and check manually to avoid TOCTOU
@SuppressWarnings("unchecked")
E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
for (int i = 0; i < input.length; i++) {
tmp[i] = Objects.requireNonNull(input[i]);
}
elements = tmp;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public int size() {
return elements.length;
}
@Override
public E get(int index) {
return elements[index];
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("not serial proxy");
}
private Object writeReplace() {
return new CollSer(CollSer.IMM_LIST, elements);
}
}
List.of(), Collections.emptyList() 공통점
둘 다 불변이기 때문에 add, remove등의 작업을 수행하려고 하면 예외가 발생합니다.
List.of(), Collections.emptyList() 차이점
get()
List.of()의 경우 생성 시에 리스트에 원소를 설정할 수 있기 때문에 get메서드 호출 시, 예외가 발생하지 않습니다.
Collections.emptyList()의 경우 내부에 원소가 있을 수 없기 때문에 get()호출 시 UnsupportedOperationException 발생합니다.
size()
List.of()는 위와 마찬가지로 생성 시 원소 설정이 가능하기 때문에 아래 코드와 같이 동작합니다.
public int size() {
return elements.length;
}
Collections.emptyList()는 비어있기 때문에 무조건 0을 반환합니다.
public int size() {return 0;}
이 외에도 isEmpty(), contains() 등등의 메서드에서 차이가 있습니다.
결론
List.of()와 Collections.emptyList()를 아래와 같이 사용할 것 같습니다.
- 만약 정말 아무것도 없는 불변 리스트를 반환하고 싶으면 Collections.emptyList()사용
- 불변이지만 내부 원소를 선언하고 싶으면 List.of()사용
'Language > JAVA' 카테고리의 다른 글
[JAVA] Jackson사용 시 primitive boolean주의점 (1) | 2023.07.23 |
---|---|
[JAVA] generic에는 왜 primitive type을 쓸 수 없나? (0) | 2023.04.02 |
eqauls, hashCode(feat. 동일성과 동등성) (0) | 2023.03.04 |
[JAVA] HashMap을 이용해 Enum인스턴스 조회하기 (0) | 2022.12.13 |
[JAVA] Array를 stream으로 (Arrays.stream() vs Stream.of()) (0) | 2022.10.28 |