在最近做的一个项目中对List进行操作时报错java.lang.UnsupportedOperationException,后来发现操作的List是由数组转换而成的,通过看源码发现问题,并写测试程序如下。
代码块:
1. public class Unsupported { 2. 3. static void test(String msg, List<String> list) { 4. System.out.println("---- " + msg + "----"); 5. Collection<String> c = list; 6. Collection<String> subList = list.subList(1, 8); 7. // 复制子List 8. Collection<String> c2 = new ArrayList<>(subList); 9. try { 10. c.retainAll(c2); 11. } catch (Exception e) { 12. System.out.println("retainAll()方法的异常: " + e); 13. } 14. try { 15. c.removeAll(c2); 16. } catch (Exception e) { 17. System.out.println("removeAll()方法的异常: " + e); 18. } 19. try { 20. c.clear(); 21. } catch (Exception e) { 22. System.out.println("clear()方法的异常: " + e); 23. } 24. try { 25. c.add("X"); 26. } catch (Exception e) { 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编程思想(第四版)