JaeWon's Devlog
article thumbnail
반응형

Arrays.asList(배열)는 String[], int[] 등의 배열을 List로 바꿀 때 자주 사용하던 메소드였다.

평소에는 배열로만 바꾸고, 데이터를 추가하지 않아서 몰랐는데 이번에 데이터를 추가하다가 아래와 같은 에러가 발생했다.

java.lang.UnsupportedOperationException: null
	at java.util.AbstractList.add(AbstractList.java:148) ~[na:1.8.0_201]
	at java.util.AbstractList.add(AbstractList.java:108) ~[na:1.8.0_201]
	at com.study.jaewonstudy.webservice.web.java.errortest.controller.ErrorTestRestController.list(ErrorTestRestController.java:23) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.5.jar:5.3.5]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.5.jar:5.3.5]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.5.jar:5.3.5]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.5.jar:5.3.5]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.5.jar:5.3.5]

1. 에러 발생 상황

- Arrays.asList() 메소드를 통해 변환한 List에 데이터를 추가시 에러 발생.

  • 코드
public Object list() throws Exception {
        String[] strArr = {"김치", "바나나", "휴일", "데이트", "커피", "감자", "나이", "수학", "개발"};

        List<String> strList = Arrays.asList(strArr);

	// 에러 발생(java.lang.UnsupportedOperationException)
        strList.add("테스트");

    	return strList;
    }

2. 원인

- Arrays.asList() method 의 결과물은 평소에 사용하는 java.util.ArrayList 가 아니라 Arrays 안에 있는 inner class.

  • 평소 사용하는 ArrayList Class
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8683452581122892189L;
    
    ...
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
    ...
    
    public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    ...
}
  • 에러발생시 사용한 Arrays$arrayList Class
private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

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

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @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]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
    }
  • AbstractList Class
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

    protected AbstractList() {
    }

    ...
    
    //	해당 메소드가 실행되는 것이다.
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    ...
}

- 위 Arrays Class(Arrays$arrayList Class)를 살펴보면, abstractList를 상속받아 생성된 것을 확인할 수 있다.

- abstractList class에 있는 add 나 addAll 메소드를 override를 하지 않고 있다.

- 그렇기 때문에 add() 메소드를 사용하면 AbstractList Class 에 있는 add() 메소드를 수행하게 된다.

- AbstractList Class 를 살펴보면 add() 메소드 사용시 UnsupportedOperationException을 던지도록 되어있다.

3. 에러 발생시 해결법

- Arrays.asList(배열) 의 결과인 arrayList 는 기존의 java.util.ArrayList 와는 다르며, add() 메소드를 쓸 수 없다.

  1. ArrayList 로 리스트 선언
  2. 해당 List에 addAll()메소드를 사용하여, Arrays.asList로 만든 리스트 저장
  3. add() 메소드 사용
    public Object list() throws Exception {
        //  1. 에러 발생(java.lang.UnsupportedOperationException)
//        String[] strArr = {"김치", "바나나", "휴일", "데이트", "커피", "감자", "나이", "수학", "개발"};
//
//        List<String> strList = Arrays.asList(strArr);
//
//        strList.add("테스트");

        //  2. 정상 동작
        String[] strArr2 = {"김치", "바나나", "휴일", "데이트", "커피", "감자", "나이", "수학", "개발"};

        List<String> strList2 = new ArrayList<String>();
        strList2.addAll(Arrays.asList(strArr2));

        strList2.add("테스트");

	// [김치, 바나나, 휴일, 데이트, 커피, 감자, 나이, 수학, 개발, 테스트]
        System.out.println(strList2);

    	return strList2;
    }

참고

- https://blog.gangnamunni.com/post/Arrays-arrayList-ArrayList/

반응형
profile

JaeWon's Devlog

@Wonol

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!