UnsupportedOperationException 的研究

1. 场景还原

最近在设计的一个类中,有成员属性

Map<String, List<String>> headers = new HashMap<>();

在实际使用过程中,发现对该属性的value进行add()操作会抛出异常UnsupportedOperationException。感觉略有奇怪,于是便开始研究。

2. 什么时候会抛出异常

使用这段代码

Map<String, List<String>> headers = new HashMap<>();
headers.put("zjh", Arrays.asList("123"));
headers.get("zjh").add("456");

会抛出异常。
而使用这段代码

Map<String, List<String>> headers = new HashMap<>();
String[] str = {"123"};
headers.put("zjh", new ArrayList<>(Arrays.asList(str)));
headers.get("zjh").add("000");

不会抛出异常。

而在前文所述中,我设计的类内就是使用第一段代码的形式才抛出了异常。
以此,我便猜测是Arrays.asList()有问题。

3. 进一步研究

使用这段代码

List<String> lists = Arrays.asList("123","456","789","000");
lists.add("999");

会抛出异常。

查看Arrays.asList的源码,发现其返回的是对象属性是ArrayList
???
感觉没毛病啊!

之后再往下看
自定义的arraylist
原来是它自定义了一个内部类,类名是 ArrayList!

查看这个父类AbstractList的情况
其内部的add实现为

public boolean add(E e) {
    add(size(), e);
    return true;
}

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

一切真相大白,Arrays.asList方法返回的List对象内部没有实现 add 方法!

4. 更多的实验

其实,Arrays.asList方法的返回就是对传入参数数组的引用。对数组进行修改也会导致Arrays.asList的返回值发生改变。
如下代码:

String[] str = {"123","456","789","000"};
List<String> lists = Arrays.asList(str);
str[1] = "9999";
System.out.println(str[1]); // 9999
System.out.println(lists);  // [123, 9999, 789, 000]

这也可以解释为什么Arrays.asList的返回值无法add或者remove。如果允许其可以变长,那么原始的数组内容应该如何修改呢?所以最后 Java 的开发人员便使其返回一个不可修改长度的List
以上所述在源码里也能体现出来,更具体的大家可以参阅Arrays这个类的源码!

5. 解决方案

我的建议是如果对 List 内部元素会有增删的操作的话,慎用Arrays.asList方法,如果非要用,可以绕一绕。
例如:

List<String> lists = new ArrayList<>(Arrays.asList("111", "222", "333"));

如此生成的 list,可变长!
代码稍微长一点,可以使用循环来给List内增加元素。
当然,我只是提供了最简单的解决方案,具体方案要放在具体项目中选择使用。