java.lang.UnsupportedOperationException异常排查

List<String> nodes = Arrays.asList("rabbit@CentOS", "rabbit_2@CentOS", "rabbit_3@CentOS");

JSONArray res = JSONUtil.parseArray(response.body());
for (JSONObject next : res.jsonIter()) {
    String name = next.getStr("name");
    if (nodes.contains(name) && next.getBool("running")) {
        nodes.remove(name);
    }
}

调用nodes.remove时抛出异常java.lang.UnsupportedOperationException

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.remove(AbstractList.java:161)
	at java.util.AbstractList$Itr.remove(AbstractList.java:374)
	at java.util.AbstractCollection.remove(AbstractCollection.java:293)
	at com.daniel.rabbit.monitor.ClusterHealthCheck.main(ClusterHealthCheck.java:54)

跟踪堆栈

首先调用了AbstractCollection#remove方法

public boolean remove(Object o) {
    Iterator<E> it = iterator();
    if (o==null) {
        // 如果传入元素为空
        while (it.hasNext()) {
            if (it.next()==null) {
                it.remove();
                return true;
            }
        }
    } else {
        // 如果传入元素不为空
        while (it.hasNext()) {
            if (o.equals(it.next())) {
                it.remove();
                return true;
            }
        }
    }
    return false;
}

这个方法的注释中写到

Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collection's iterator method does not implement the remove method and this collection contains the specified object.

如果这个集合返回的迭代器没有实现remove方法的话,该方法会抛出一个UnsupportedOperationException

接着调用了AbstractList#remove方法

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        // 这里调用了自己的remove方法
        AbstractList.this.remove(lastRet);
        if (lastRet < cursor)
            cursor--;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
        throw new ConcurrentModificationException();
    }
}

最后调用的是AbstractList.this.remove方法

public E remove(int index) {
    throw new UnsupportedOperationException();
}

很明显,该方法必定会抛出一个UnsupportedOperationException

为什么会出现这种情况

可以看到文章开头的代码,我们使用的是Arrays#asList方法来生成List,看一下这个方法的内部实现

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

private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable {
    // ...
    
    ArrayList(E[] array) {//...}
    
    @Override
    public int size() {//...}

    @Override
    public Object[] toArray() {//...}

    @Override
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {//...}

    @Override
    public E get(int index) {//...}

    @Override
    public E set(int index, E element) {//...}

    @Override
    public int indexOf(Object o) {//...}

    @Override
    public boolean contains(Object o) {//...}

    @Override
    public Spliterator<E> spliterator() {//...}

    @Override
    public void forEach(Consumer<? super E> action) {//...}

    @Override
    public void replaceAll(UnaryOperator<E> operator) {//...}

    @Override
    public void sort(Comparator<? super E> c) {//...}
}

这里使用的是Arrays内部的ArrayList,该ArrayList继承了AbstractList,并且没有重写父类的remove方法,接着我们在循环中调用remove方法,此时调用的应该是父类AbstractList#remove方法,直接抛出UnsupportedOperationException

解决方法

将Arrays.asList换成java.util.ArrayList即可

List<String> nodes = CollectionUtil.newArrayList("rabbit@CentOS", "rabbit_2@CentOS", "rabbit_3@CentOS");