목차
개요
List 선언 시, 선언과 동시에 초기화하여 사용하기 편리하여 Arrays.asList()를 종종 사용했습니다. 편리하게 사용도중 필요에 의해 선언된 리스트 첫 원소에 새로운 원소를 더해서 사용해야하는 상황이 있어 add 메서드를 사용하였습니다. 컴파일 에러도 없기에 자신있게 소스를 실행했지만 실제 실행 단계에서 다음과 같은 예외가 발생하였습니다. "arrays.aslist unsupportedoperationexception" 이 오류의 원인을 찾던 중 Arrays.asList() 사용하는데 조심해야 할 부분을 찾게 되어 공유하고자 글을 남깁니다.
Arrays.asList()의 결과물은 Arrays 안의 inner 클래스다.
오류가 나는 부분을 디버깅 찍어보면 평소에 사용하는 java.util.ArrayList가 아니라 Arrays$arrayList로 찍히는 것을 볼 수 있습니다. 즉, Arrays안의 inner 클래스 임을 알 수 있습니다.
결론부터 말씀드리자면 아래의 과정으로 인해 unsupportedoperationexception 오류가 발생하게 됩니다.
- Arrays Class(Arrays$arrayList Class)를 살펴보면, AbstractList를 상속받아 생성된 것을 확인할 수 있다.
- abstractList class에 있는 add나 addAll메서드를 override 하고 있지 않다.
- 그렇기 때문에 add()메서드 사용 시, AbstractList Class에 있는 add() 메서드를 수행하게 된다.
- AbstractList Class를 살펴보면 add() 메서드 사용 시 UnsupportedOperationException을 던지게 되어 있다.
아래는 각 코드 입니다.
**평소 사용하는 ArrayListClass
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.asList() 사용 시 사용하게 되는 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();
}
...
}
해결법
해결법은 간단합니다.
ArraysList나 LinkedList 등의 컬렉션 클래스로 한번 Wrapping해서 사용하면 됩니다.
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
결론
Arrays.asList()를 사용 시 주의할 점에 대해 알아보았습니다. 해결법이라고 작성한 부분에서 Wrapping해 사용하면 된다고 하였지만 제일 좋은 방법은 자신이 사용하려는 리스트가 고정된 리스트인지 원소에 변경이 있을 리스트인지 구분해서 적절한 초기화 방법을 처음부터 사용하는 것이 좋을 것 같다고 생각합니다.
https://yonguri.tistory.com/137
https://dev-jwblog.tistory.com/72
https://blog.gangnamunni.com/post/Arrays-arrayList-ArrayList/
'Java' 카테고리의 다른 글
| List에서 특정 조건 만족하는 요소 삭제하기 (List.removeIf 사용하기) (0) | 2025.01.17 |
|---|---|
| 상속관계의 클래스 일 때, @Builder 사용하기 (0) | 2024.11.16 |
| ObjectMapper, Pattern 등의 클래스를 싱글톤으로 사용해도 될지 고민했던 내역 (0) | 2024.11.15 |
| 인텔리제이에서 자바 버전 변경하기 (0) | 2023.10.17 |
| Java에서의 HashMap 동작 원리 파악하기 (0) | 2023.07.27 |
