new ArrayList<>(), Arrays.asList(), List.of()

yboy·2022년 12월 20일
1

Learning Log 

목록 보기
28/41
post-thumbnail
post-custom-banner

학습동기

코딩테스트 문제를 푸는 중에 Map을 사용해야 할 때가 있었다. Map의 value 타입으로 List를 사용했는데 이때 value를 초기화할 때 Arrays.asList()를 사용하였다. 하지만 Arrays.asList()로 생성된 List에서 값이 추가되지 않고 UnsupportedOperationException 예외가 발생하는 것이였다...
List.of()는 불변 객체를 만들고 Arrays.asList()는 변경 가능한 리스트를 만들어 주는 것이라고 생각했는데 기존에 알고 있던 게 틀렸던 것!!
그래서 학습을 다짐하게 되었다.

학습내용

우선 가장 기본이 되는 new ArrayList<>() 부터 알아보자.

new ArrayList<>()

 List<Integer> list  = new ArrayList<>();

new ArrayList<>()는 ArrayList를 생성하는 가장 기본이 되는 방법이다.
return type은 java.util.ArrayList의 ArrayList이다. return type은 ArrayList지만 보통 다형성을 이용하기 위해 List interface로 선언한 변수를 사용해 이용한다.

Arrays.asList()

 List<Integer> list = Arrays.asList();

List는 new ArrayList<>() 말고도 Arrays.asList()로도 생성할 수 있다.

🧐그럼 일반적으로 사용하는 new ArrayList<>()와 어떤 차이가 있을까?

1. return type이 java.util.Arrays의 정적 클래스인 ArrayList이다.

<Arrays.asList()로 생성되는 ArrayList>

 private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
 

<new ArrayList<>()로 생성되는 ArrayList>

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

return type이 다르지만 List 타입으로 객체를 받을 수 있는 이유는 둘 다 AbstractList를 구현하고 있기 때문이다.

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> 

AbstractList는 List를 구현하고 있다. 즉 다형성을 상위 타입으로 객체를 받는 것이다.

2. 원소의 추가와 삭제가 불가능하다.

new ArrayList<>()는 add, remove 등 원소 추가, 삭제가 가능하지만 Arrays.asList()는 추가, 삭제가 불가능하다. 만약 추가, 삭제 하려고 한다면 java.lang.UnsupportedOperationException 예외가 발생한다.
실제 Arrays.ArrayList를 살펴보면 add()와 remove()가 구현되어 있지 않은 것을 살펴볼 수 있다.

여기까지 살펴보면 보통 테스트 픽스쳐를 만들 때는 픽스쳐가 외부에서 변경되는 것을 막을 수 있기 때문에 Arrays.asList()로 List를 생성하면 이점이 있어보인다. 하지만...

      @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

Arrays.ArrayList는 set 메서드를 구현하고 있어 불변 객체를 보장해줄 수는 없다. 그럼 완벽하게 불변객체를 보장하려면 어떻게 해야 할까?

List.of()

List.of()는 자바9버전부터 생긴 List를 만드는 또 다른 방법이다.

  static <E> List<E> of(E e1) {
        return new ImmutableCollections.List12<>(e1);
    }

List.of()는 ImmutableCollections를 이용해 List를 생성함으로써 생성된 List의 불변성을 보장해준다.
예를 들어 set 메서드로 list의 값을 수정한다면 UnsupportedOperationException이 발생한다.
ImmutableCollections는 내부의 List12 static 클래스로 List를 생성해서 객체의 불변성을 보장해주는데 List12는 AbstractImmutableList를 상속 받고 있다.

    static abstract class AbstractImmutableList<E> extends AbstractImmutableCollection<E>
            implements List<E>, RandomAccess {

        // all mutating methods throw UnsupportedOperationException
        @Override public void    add(int index, E element) { throw uoe(); }
        @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
        @Override public E       remove(int index) { throw uoe(); }
        @Override public void    replaceAll(UnaryOperator<E> operator) { throw uoe(); }
        @Override public E       set(int index, E element) { throw uoe(); }
        @Override public void    sort(Comparator<? super E> c) { throw uoe(); }
 static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }

AbstractImmutableList는 위 처럼 객체의 상태를 변경하는 작업에 대해 UnsupportedOperationException 예외를 발생시켜 불변을 보장해준다.

따라서 테스트 픽스쳐를 만들때나 Thread safe를 보장하기 위해 불변 객체를 사용해야 하는 상황에서는 new ArrayList<>()나 Arrays.asList()보다 List.of()를 사용하는 것이 적합하다.

마무리

모호하게만 알고 있었던 List 생성법에 대해 학습해보는 시간이였다. 평소 별거 아니라고 생각하고 넘어갔던 주제였는데 이런 기본적이지만 실수할 수 있는 것들을 한 번 짚고 넘어가는 습관을 계속해서 길러 나가자!!

post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 12월 28일

yboy

1개의 답글