原文网址:Java的List之坑系列--Collections#unmodifiableList仍然可变_IT利刃出鞘的博客-CSDN博客
简介
说明
本文介绍Collections#unmodifiableList的坑:本不可变的集合却被改变了。
Collections#unmodifiableList是保护集合的,让集合不可变,本文介绍它被改变的情况。
Collections#unmodifiableList简介
为了防止 List 集合被误操作,可用Collections#unmodifiableList生成一个不可变(immutable)集合,进行防御性编程。这个不可变集合只能被读取,不能做写操作,包括增加,删除,修改,从而保护不可变集合的安全。
原理:返回的集合没有实现增删改的方法。
示例:
package org.example.a;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "tree"));
List<String> unmodifiableList = Collections.unmodifiableList(list);
// 以下三行都会抛出UnsupportedOperationException 异常
unmodifiableList.add("four");
unmodifiableList.remove(1);
unmodifiableList.set(0, "test");
}
}
结果
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055)
at org.example.a.Demo.main(Demo.java:12)
问题复现
如果使用不当,Collections#unmodifiableList操作后的List也是可能会被改变的。
代码
package org.example.a;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "tree"));
List<String> unmodifiableList = Collections.unmodifiableList(list);
list.set(0, "one_modified");
System.out.println(unmodifiableList.get(0));
}
}
执行结果
one_modified
可见,被修改了!
原因分析
Collections#unmodifiableList
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
implements List<E> {
private static final long serialVersionUID = -283967356065247728L;
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
...
}
跟前边subList方法一样的情况:新集合底层实际使用(引用)了原始 List。
解决方法
法1:JDK1.9的List#of 方法
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = List.of(list.toArray(new String[]{}));
法2:Guava包的ImmutableList
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = ImmutableList.copyOf(list);