为了防止 List 集合被误操作,我们可以使用 Collections#unmodifiableList 生成一个不可变(immutable)集合,进行防御性编程。
这个不可变集合只能被读取,不能做任何修改,包括增加,删除,修改,从而保护不可变集合的安全。
上面最后三行写操作都将会抛出 UnsupportedOperationException 异常
但是你以为这样就安全了吗?
如果有谁不小心改动原始 List,你就会发现这个不可变集合,竟然就变了。。。
上面单元测试结果将会全部通过,这就代表 Collections#unmodifiableList 产生不可变集合将会被原始 List 所影响。
查看 Collections#unmodifiableList 底层实现方法:
可以看到这跟上面 SubList 其实是同一个问题,新集合底层实际使用了原始 List。
由于不可变集合所有修改操作都会报错,所以不可变集合不会产生任何改动,所以并不影响的原始集合。但是防过来,却不行,原始 List 随时都有可能被改动,从而影响不可变集合。
可以使用如下两种方式防止上卖弄的情况。
使用 JDK9 List#of 方法。
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = List.of(list.toArray(new String[]{}));
使用 Guava immutable list
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = ImmutableList.copyOf(list);
相比而言 Guava 方式比较清爽,使用也比较简单,推荐使用 Guava 这种方式生成不可变集合。
foreach 增加/删除元素大坑
先来看一段代码:
String[] arrays = {"1", "2", "3"};
List<String> list = new ArrayList<>(Arrays.asList(arrays));
for (String str : list) {
if (str.equals("1")) {
list.remove(str);
}
}
上面的代码我们使用 foreach 方式遍历 List 集合,如果符合条件,将会从集合中删除改元素。
这个程序编译正常,但是运行时,程序将会发生异常,日志如下:
java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)