JaeWon's Devlog
article thumbnail
반응형

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

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

<java />
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. 1. 에러 발생 상황

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

  • 코드
<java />
public Object list() throws Exception { String[] strArr = {"김치", "바나나", "휴일", "데이트", "커피", "감자", "나이", "수학", "개발"}; List<String> strList = Arrays.asList(strArr); // 에러 발생(java.lang.UnsupportedOperationException) strList.add("테스트"); return strList; }

2. 2. 원인

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

  • 평소 사용하는 ArrayList Class
<java />
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
<java />
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
<java />
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. 3. 에러 발생시 해결법

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

  1. ArrayList 로 리스트 선언
  2. 해당 List에 addAll()메소드를 사용하여, Arrays.asList로 만든 리스트 저장
  3. add() 메소드 사용
<java />
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

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