【Java异常】java.lang.UnsupportedOperationException解决方案
在最近做的一个项目中对List进行操作时报错java.lang.UnsupportedOperationException,后来发现操作的List是由数组转换而成的,通过看源码发现问题,并写测试程序如下。
代码块:
public class Unsupported {
static void test(String msg, List<String> list) {
System.out.println("---- " + msg + "----");
Collection<String> c = list;
Collection<String> subList = list.subList(1, 8);
// 复制子List
Collection<String> c2 = new ArrayList<>(subList);
try {
c.retainAll(c2);
} catch (Exception e) {
System.out.println("retainAll()方法的异常: " + e);
}
try {
c.removeAll(c2);
} catch (Exception e) {
System.out.println("removeAll()方法的异常: " + e);
}
try {
c.clear();
} catch (Exception e) {
System.out.println("clear()方法的异常: " + e);
}
try {
c.add("X");
} catch (Exception e) {
System.out.println("add()方法的异常: " + e);
}
try {
c.addAll(c2);
} catch (Exception e) {
System.out.println("addAll()方法的异常: " + e);
}
try {
c.remove("C");
} catch (Exception e) {
System.out.println("remove()方法的异常: " + e);
}
// list.set() 方法修改值,但是没有改变数据结构的大小
try {
list.set(0, "X");
} catch (Exception e) {
System.out.println("list.set()方法的异常: " + e);
}
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));
test("复制操作: ", new ArrayList<String>(list));
System.out.println("**********一条华丽的分割线**********");
test("Arrays.asList的操作: ", list);
test("unmodifiableList()的操作: ", Collections.unmodifiableList(list));
}
}
执行结果:
---- 复制操作: ----
**********一条华丽的分割线**********
---- Arrays.asList的操作: ----
retainAll()方法的异常: java.lang.UnsupportedOperationException
removeAll()方法的异常: java.lang.UnsupportedOperationException
clear()方法的异常: java.lang.UnsupportedOperationException
add()方法的异常: java.lang.UnsupportedOperationException
addAll()方法的异常: java.lang.UnsupportedOperationException
remove()方法的异常: java.lang.UnsupportedOperationException
---- unmodifiableList()的操作: ----
retainAll()方法的异常: java.lang.UnsupportedOperationException
removeAll()方法的异常: java.lang.UnsupportedOperationException
clear()方法的异常: java.lang.UnsupportedOperationException
add()方法的异常: java.lang.UnsupportedOperationException
addAll()方法的异常: java.lang.UnsupportedOperationException
remove()方法的异常: java.lang.UnsupportedOperationException
list.set()方法的异常: java.lang.UnsupportedOperationException
产生的原因
调用Arrays.asList()生产的List的add、remove方法时报异常,这是由Arrays.asList() 返回的是Arrays的内部类ArrayList, 而不是java.util.ArrayList。Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList,remove、add等方法AbstractList中是默认throw UnsupportedOperationException而且不作任何操作。java.util.ArrayList重新了这些方法而Arrays的内部类ArrayList没有重新,所以会抛出异常。解决方法如下:
通过再新建一个ArrayList,让这些方法重新实现AbstractList的方法,如下图:
以下内容摘自Java编程思想(第四版):
因为Arrays.asList()会生成一个List,它基于一个固定大小的数组,而这个数组仅支持那些不会改变数组大小的操作。任何会引起对底层数据结构的尺寸进行修改的方法都会产生一个UnsupportedOperation Exception异常,以表示对未获支持操作的调用(一个编程错误)。
注意,应该把Arrays.asList()的结果作为构造器的参数传递给任何Collection(或者使用addAll()方法,或者 Collections.addAll()静态方法),这样可以生成允许使用所有的方法的普通容器----这在 main()方法中的第一个对test的调用中得到了展示,这样的调用会产生新的尺寸可调的底层数据结构。 Collections类中的“不可修改”的方法将容器包装到了一个代理中,只要你执行任何试图修改容器的操作,这个代理都会产生UnsupportedOperationException异常。使用这些方法的目标就是产生“常量”容器对象。“不可修改”的Collections方法的完整列表将在稍后介绍。
test方法中的最后一个try语句块将检查作为List的一部分的set()方法。这很有趣,因为你可以看到“未获支持的操作”这一技术的粒度来的是多么方便----所产生的“接口”可以在Arrays. asListCollections()返回的对象和Collections.unmodifiableList()返回的对象之间,在一个方法的粒度上产生变化。 Arrays.asList()返回固定尺寸的List,而 Collections. unmodifiableListO产生不可修改的列表。正如从输出中所看到的,修改 Arrays. asList()返回的List中的元素是可以的,因为这没有违反该List“尺寸固定”这一特性但是很明显,unmodifiableList()的结果在任何情况下都应该不是可修改的。如果使用的是接口,那么还需要两个附加的接口,一个具有可以工作的set()方法,另外一个没有,因为附加的接口对于 Collection的各种不可修改的子类型来说是必需的。
【参考资料】
1、Java编程思想(第四版)