Java从入门到放弃(七)集合框架之ArrayList的坑
2018-04-06 21:24
597 查看
1、快速失败和安全失败
1)、快速失败
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出ConcurrentModificationException。如下: List<Integer> list = new ArrayList<>();for(int i = 0;i < 10;i++){
list.add(i);
}
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer next = iterator.next();
if(next==3){
list.remove(6);
}
System.out.println(next);
}输出结果为:
0 1 2 3 Exception in thread "main" 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) at Main.main(Main.java:7)原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值(具体可看Java从入门到放弃(五)集合框架之ArrayList源码(2))。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
所以不要在迭代遍历的时候直接删除集合内的元素,如果要删除可以使用迭代器内部的方法:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); 检查modCount的值是否发生改变
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; //更新expectedModCount的值,
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}因为内部的remove方法会同步更新expectedModCount的值,使用不会发生快速失败的异常。
2)、安全失败
安全失败是在迭代的时候对集合进行更新的时候不操作原集合,而是把集合进行拷贝一份,在复制的集合上进行修改操作,修改完之后再更改集合对象的引用。所以就避免了ConcurrentModificationException异常,不过因为不是再原集合上操作,所以在迭代的时候如果对集合进行了修改,迭代器是不能检测到的,
3)、应用场景
java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),如:ArrayList,LinkedListjava.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。如:CopyOnWriteArrayList。
2、包装类的坑
Integer delete = 6; List<Integer> list = new ArrayList<>(); for(int i = 0;i < 10;i++){ list.add(i); } list.remove(delete); System.out.println(list.toString()); //[0,1,2,3,4,5,7,8,9]因为remove重载有两个方法,一个remove(int),一个是remove(object),代码中的delete是Integer类型,所以会视为Object,直接删除数组中的6这个元素,而不是索引为6的元素。
long delete = 6; List<Integer> list = new ArrayList<>(); for(int i = 0;i < 10;i++){ list.add(i); } list.remove(delete); System.out.println(list.toString()); //[0,1,2,3,4,5,6,7,8,9]这里的delete虽然是基本数据类型,但是remove(int)方法的参数是int,long不能自动转换为int类型,需要强制转换,所以list.remove(delete)是会使用list.remove(object)方法,因为参数是Long类型,集合内数据是Integer类型,所以找不到对应的相等的元素,没有删除任何元素。
3、ListIterator和Iterator的区别
Iterator迭代器的方法: hasNext():判断迭代器指向的位置后面是否还有元素,有就返回true,没有返回false; next():取出迭代器指向位置后面的一个元素; remove():删除迭代器指向位置的元素。 checkForComodification():检查快速失败的方法;ListIterator迭代器的方法: add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前
hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false
hasPrevious():如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false
next():返回列表中ListIterator指向位置后面的元素
nextIndex():返回列表中ListIterator所需位置后面元素的索引
previous():返回列表中ListIterator指向位置前面的元素
previousIndex():返回列表中ListIterator所需位置前面元素的索引
remove():从列表中删除next()或previous()返回的最后一个元素
set(E e):从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e
异同点: 1、Iterator适用于全部的集合类,而ListIterator只能适用于List及其之类使用
2、Iterator迭代器只能向后遍历,而ListIterator不仅可以向后遍历,也能向前遍历。
3、ListIterator可以定位当前位置的索引,而Iterator不能
4、ListIterator可以remove,add,set方法对集合进行修改,而Iterator只有remove方法;
相关文章推荐
- Java从入门到放弃(六)集合框架之ArrayList源码(2)
- Java从入门到放弃(五)集合框架之ArrayList源码(1)
- JavaSE入门学习35:Java集合框架之List接口及其实现类ArrayList和LinkedList
- java源码分析之集合框架 ArrayList 03
- Java:集合框架详解(ArrayList)和代码示例
- java入门第三季(四、Java中的集合框架)
- Java:集合框架详解(ArrayList)和代码示例
- java - 集合框架(二)-LinkedList , ArrayList
- JAVA中的集合框架(ArrayList)
- Java集合框架学习(五) ArrayList详解
- java 集合框架--ArrayList 和 Vector区别
- Java 回顾笔记_集合框架之_linkedList_arrayList
- 《Java从入门到放弃》框架入门篇:Struts2的常用验证方式(二)
- JAVA之旅(十八)——基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用
- JavaSE入门学习38:Java集合框架之迭代器
- 《Java从入门到放弃》框架入门篇:springMVC数据传递
- Java入门_集合框架
- 使用Java实现面向对象编程--集合框架-->Vector和ArrayList
- [置顶] Java基础你要知道的事情------集合框架之ArrayList
- java核心之集合框架——ArrayList源码分析