자바의 List는 배열을 넘어서서 동적으로 데이터들을 순서가 있고 중복이 허용되게 하며

1차원적으로 저장할 수 있게 하는 Collection이다.

public interface List<E> extends Collection<E> {
    // Query Operations

    /**
     * Returns the number of elements in this list.  If this list contains
     * more than {@code Integer.MAX_VALUE} elements, returns
     * {@code Integer.MAX_VALUE}.
     *
     * @return the number of elements in this list
     */
    int size();
... 생략

List는 자바의 Collection Interface를 구현한 "인터페이스"이며

ArrayList, LinkedList, Stack, Vector로 인스턴스화를 할 수 있다.

 

이 포스팅에선 List를 인스턴스화하여 선언할 수 있는 몇 가지의 방법과 그에 대한 특징에 대해 알아볼 것 이다.

 

new를 이용한 ArrayList 선언

new를 이용하여 ArrayList를 선언하게 되면 동적으로 List의 크기를 관리할 수 있다는 장점이 있다.

new ArrayList<T>(n); 에서 n 부분에 아무것도 적지 않으면 기본 생성자로 선언하게 되면

10개의 용량을 가지는 리스트가 생성되고 ( size가 0인 ArrayList는 기본 설정값인 10으로 capacity가 초기화 됨)

n에 숫자를 적게 되면 그 숫자 만큼의 용량이 초기 용량 값으로 생성된다.

 

앞서 말한 List의 크기를 동적으로 관리할 수 있는 것이 장점이자 단점이 되는데

큰 단점을 말하자면 매개변수n 부분에 적은 초기 용량값을 초과하는 객체가 들어오면

ArrayList는 자동으로 리사이징 되어 ArrayList가 차지하는 메모리 용량이 1.5배 늘어나게 된다.

이때 지금까지의 크기 비율로 매번 증가하는 용량의 값이 다른데 이것을 자꾸 늘리다 보면 사용하는

메모리의 낭비가 심해 단점이 될 수 있고 공간 확장에 드는 행위 때문에 작업 처리 시간이 증가하게 된다.

이러한 상황을 피하기 위해 사용할 List의 용량을 미리 예상하여 정하거나

아래의 메소드를 활용하여 효과적인 List 메모리 관리를 할 수 있다.

 

ArrayList의 용량을 관리하는 방법으로 ArrayList에 내장된 trimToSize 메서드가 있는데

    /**
     * Trims the capacity of this {@code ArrayList} instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an {@code ArrayList} instance.
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

이 메서드는 ArrayList의 용량을 현재의 List Size(현재의 리스트 크기)만큼 동일하게 변경한다.

즉 이 메서드로 내부에 할당되지 않은 공간을 현재의 크기 만큼 잘라버려 메모리를 아낄 수 있다.

 

또한 ensureCapacity(int minCapacity) 라는 메서드로 작업이 진행되는 도중

ArrayList의 최소 용량의 값을 원하는 값으로 늘려 ArrayList의 자동 1.5배 리사이징을 방지하는 작업을 할 수 있다.

 

 

Immutable한 List를 만들기 위한 List.of() ? Arrays.asList() ? Collections.unmodifiableList() ? Collections.singletonList() ?

먼저 List.of()는 Java 8 이후에 업데이트 된 메소드 이다.

Arrays.asList()가 있는데 뭐하러 쓰는가에 대해 궁금증이 생기면서 부터 정리하게 된 내용이다.

먼저 결론부터 말하자면 완전히 Immutable한 리스트를 만들기 위해 사용해야하는 메소드는 List.of()이다.

List.of()는 매개변수로 받은 Element들을 Immutable하게 관리해주는 ImmutableCollections 객체로 만들어

새로운 Element들을 CUD하려고 하면 UnsupportedOperationException을 발생시킨다.

추가로 List.of()의 E에 null을 넣을시 NullPointException이 일어난다. null을 허용하지 않는다.

 

그럼 Arrays.asList()도 Immutable한 List를 만들 수 있지 않냐라고 했을때 불변하지 않다고 말할 수 있다.

이를 알기 위해 Arrays.asList의 구조부터 살펴보자.

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

위 코드는 Arrays.asList의 내장 함수 구현체 이다. ArrayList<>(a);를 반환하도록 되어있는데 

여기서 주의해야 할 점은 이 ArrayList는 java.util 패키지의 ArrayList가 아닌 그 일부만 구현 된 Arrays 내부 클래스이다.

확인해보면 add() 메소드 remove등이 없다.

그런데 이 ArrayList는 add도 remove도 없는데 왜 불변이 아니냐

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

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i++) {
                a[i] = operator.apply(a[i]);
            }
        }

Arrays.asList를 호출 했을 때 반환되는 Arrays의 내부 클래스인 ArrayList에 구현 되어 있는 위 메서드 때문에

List의 Element 값들의 위치가 변경되거나 값이 변경 될 수 있기에 불변하지 않다 라고 말할 수 있다.

 

그래서 위의 asList를 사용하고 난 뒤에는 Collections.unmodifiableList()의 매개변수로

List를 주어 immutable 하도록 만들어 준다고 하는데 이 조차도 이 메소드는 원본 List를 참조하고 있기 때문에

원본 List의 값이 변경되면 해당 메서드를 사용하여 불변하게 만들어준 List의 값도 바뀌게 된다.

이러면 불변하다고 말을 할 수 없다.

List usedAsList = Arrays.asList(1,2,3,4,5);
List usedUnmodifiableList = Collections.unmodifiableList(usedAsList);

System.out.println(usedAsList);
System.out.println(usedUnmodifiableList);

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

의 결과 값을 도출하는데 위의 usedAsList의 값이 바뀌게 되면  똑같이 usedUnmodifiableList의 리스트 값도

원본을 참조하여 바뀌게 된다. 따라서 불변히라고 말을 할 수 없다.

 

강력한 불변을 보장해주는 메서드는 List.of()이다.

하지만 Element 인자의 개수가 1개인 상황에 쓸 수 있는 불변을 제공하는 List가 있는데

이게 바로 Collections.singletonList()이다. 이는 배열 변환 과정이 거의 없고 주소값과 속성에 대해 불변을

보장해준다. 이는 1개인 상황에서 메모리 절약에 효과적이라고 한다.  또한 singletonList()는 null에 대한 지원을 

하므로 상황에 따라 잘 쓰면 될 것 같다. 

 

  • ✔️ 메서드가 지원됨을 의미합니다
  • ❌ 이 메서드를 호출하면 UnsupportedOperationException
  • ❗️ 메서드의 인수가 변형을 일으키지 않는 경우에만 메서드가 지원됨을 의미합니다.

사진 출처 : Singleton List Showdown: Collections::singletonList Vs. List::of - DZone Java 

+ Recent posts