Java 对 Arrays.asList 返回的列表调用迭代器 remove 报 UnsupportedOperationException
Arrays.asList 返回固定大小的数组视图,迭代器不能删除元素。按需复制为 ArrayList,并检查原数组关联、元素引用与迭代器调用顺序。
遍历列表时通过迭代器删除元素,通常能避免直接修改列表引起的迭代冲突。但如果列表来自 Arrays.asList,下面的代码仍会报 UnsupportedOperationException:
List<String> values = Arrays.asList("1", "2", "3", "4");
Iterator<String> iterator = values.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if ("2".equals(value)) {
iterator.remove();
}
}
异常堆栈可能出现 AbstractList.remove 或相应迭代器实现。将 iterator() 换成 listIterator() 并不会让底层列表支持删除。
该问题在 Java 7 场景中就存在;当前 Java SE 的 Arrays.asList 仍返回固定大小、由数组支持的列表。下文完整示例采用 Java 7 可用的写法。
先检查列表在哪里创建
List 是接口,不能仅凭变量声明为 List<String> 就认定它支持增删。追溯创建语句,确认是不是 Arrays.asList(...) 的返回值,以及中途有没有转换成其他集合。
固定大小不等于所有修改都禁止。对于这里的字符串数组,修改已有位置可以反映到底层数组:
String[] source = {"1", "2", "3"};
List<String> view = Arrays.asList(source);
view.set(1, "x");
System.out.println(source[1]); // 预期为 x
删除或新增元素会改变大小,不能在这个视图上实现。如果传入数组的实际组件类型比列表声明类型更窄,替换不兼容类型还可能遇到 ArrayStoreException;这与本文的删除限制是不同问题。
因此,不要将 set 成功当作列表支持 remove 的证据。对于 UnsupportedOperationException,应先确认集合的操作约定,而不是一律改成并发容器。
在需要删除之前创建 ArrayList
需要独立、可改变大小的列表时,在创建阶段复制元素:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class RemoveExample {
public static void main(String[] args) {
String[] source = {"1", "2", "3", "4"};
List<String> values =
new ArrayList<String>(Arrays.asList(source));
Iterator<String> iterator = values.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if ("2".equals(value)) {
iterator.remove();
}
}
System.out.println(values);
System.out.println(Arrays.toString(source));
}
}
按示例逻辑,列表应为 [1, 3, 4],原数组仍为 [1, 2, 3, 4]。新列表的大小和元素位置独立于原数组;采用这一改法前,要确认调用方接受这种变化。
这里复制的是元素引用,不是对任意元素做深拷贝。字符串示例不会涉及可变对象内部状态;若元素是业务对象,修改对象本身仍可能被其他持有同一引用的代码看到。
换删除方法不能补上集合缺失的能力
先收集待删元素,再对原来的固定大小列表调用 removeAll,仍然没有解决底层列表不能缩小的问题。即使某次因为没有匹配项而没有抛错,也不能证明真正删除时受支持。
迭代器自身也有调用顺序要求:一次 next() 之后最多调用一次 remove()。尚未取出元素,或对同一项连续删除,可能触发 IllegalStateException。这类错误应检查迭代流程,不能与固定大小列表的异常混为一谈。
修复时检查“有匹配项、无匹配项、空列表”三种输入,并确认删除后的列表内容及原数组是否符合预期。若调用方原本需要数组与列表同步变化,直接加一层 ArrayList 会改变这个约定,应让调用方明确接收过滤后的新列表或新数组。
保留原有创建语句和测试数据便于回退。若实际业务已经删除了可变列表中的内容,仅回退代码不会恢复数据,应从事先保留的数据副本恢复;浅拷贝列表不能恢复元素内部的修改。


