简介
说明
本文介绍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);
请先
!