记录一下看到的关于ArrayList的面经
1. ArrayList迭代过程删除问题:
List<String> list = new ArrayList<String>();
2 list.add("1");
3 list.add("2");
list.add("3");
4 for (String item : list) {
5 System.out.println("item:" + item);
6 if ("1".equals(item)) {
7 list.remove(item); // java.util.ConcurrentModificationException 修改时异常
8 }
if ("2".equals(item)) {
7 list.remove(item); // 无报错
8 }
-
使用foreach是容易报修改时异常的原因分析:当使用ArrayList.foreach 遍历时,其实会编译成迭代器
Iterator.里面维护了两个变量 modcount 和 expectmodcount
如果两个数不等,就报错。ArrayList#remove 之后将会使 modCount
加一,遍历下一个时,expectedModCount与 modCount将会不相等,这就导致迭代器遍历时将会抛错。 -
不会报错的情况:所以只要满足条件:删除的元素在循环时的指针cursor+1=size就会出现这种情况!删除ArrayList倒数第二个(即第
size - 1个元素)就会出现不抛异常的假象。因为此时size ==
cursor,相当于循环退出了。(例如size=3,删除第2个元素;size=4,删除第3个元素) -
解决方法:使用安全的集合 copyonWriteList; 或者是直接用迭代器删除
Iteartor<String> iterator = list.Itreator();
while(iterator.hasNext()){
String a = iterator.Next();
if("1".equals(a)){
iterator.remove();
}
}
第二种方法: list.removeIf(str->(){str.equals("1");})
2. 李逵还是李鬼???
String[] array = {"a","b","c"};
ArrayList<String> list = Arrays.asList(array); //此处返回的是ArrayList?
调用一下add
list.add("d"); // 报错了 UnsupportedOperationException。
再看一下Arrays.asList()的源码:
- 发现这里返回的ArrayList并不是java.utils.ArrayList ,而是Arrays自带的一个内部类 java.utils.Arrays$ArrayList,是李鬼不是李逵!!!
- 虽然李鬼也是实现了抽象list类,但是没有重写add、remove等方法。
真正的李逵ArrayList实现了,而李鬼没有,就调用父类的方法直接报错。 - 此外,李鬼还有bug,他和那个array数组是共享一个底层list,
结果:
arrays:[modify_1, modify_2, 3]
list:[modify_1, modify_2, 3]
从日志输出可以看到,不管我们是修改原数组,还是新 List 集合,两者都会互相影响。
为了避免这个bug,可以套个娃 :ArrayList list = new ArrayList(Arrays.asList(array));
3. List 切片 sublist:subList 生成新集合也会与原始 List 互相影响。
integerList:[10, 20, 3]
subList:[10, 20]
可以发现这个 SubList 内部有一个 parent 字段保存保存最原始 List 。所有外部读写动作看起来是在操作 SubList ,实际上底层动作却都发生在原始 List 中
4. 不可变集合的漏洞
上面最后三行写操作都将会抛出 UnsupportedOperationException 异常,但是如果对list操作,照样可以修改。原因和问题三一样,list和unmodifiableList底层是同一个list
- 解决方法:
使用 JDK9 List#of 方法。
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = List.of(list.toArray(new String[]{}));