(三十七)什么是迭代器和iterator.remove()与list.remove(obj)源码解释
为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator).
迭代器是一个对象,他的工作是遍历并选择序列中的对象,他提供了一种访问一个容器对象中各种元素。
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
一、Iterator
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
①remove方法:要删除一个元素,指针必须先移动到后面,否则抛IllegalStateException异常
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
ArrayList<String> al=new ArrayList<String>();
al.add("001");
al.add("002");
al.add("003");
Iterator<String> it1=al.iterator();
it1.next();
//IllegalStateException - 如果尚未调用 next 方法,或者在上一次调用 next 方法之后已经调用了 remove 方法。
it1.remove();
Iterator<String> it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
②ConcurrentModificationException是在遍历时,对集合进行增减操作,出现的问题或多线程操作导致的,当一个线程使用迭代器遍历容器的同时,另外一个线程对这个容器进行增减操作
Iterator<String> it=al.iterator();
while(it.hasNext())
{
String s=(String)it.next();
al.remove(s);
}
单线程这种问题的解决方法:遍历的过程中将需要删除的对象保存在一个集合中,等遍历结束后在调用removeAll()方法来删除或者用iter.remove()删除
多线程解决方法:
①在使用迭代器遍历容器时对容器的操作放到synchronized代码块中
②jdk1.5版本引入了线程安全的容器,比如ConcurrentHashMap和CopyOnWriteArrayList等。可以使用这些线程安全的容器来代替非线程安全的容器。
public class HelloWorld {
public static void main(String []args) {
ArrayList<String> al=new ArrayList<String>();
ArrayList<String> rem=new ArrayList<String>();
al.add("001");
al.add("002");
al.add("003");
al.add("004");
Iterator<String> it=al.iterator();
while(it.hasNext())
{
String s=(String)it.next();
rem.add(s);
}
System.out.println(al.removeAll(rem));//true
System.out.println(al.size());//0
}
}
利用迭代器输出元素:https://blog.csdn.net/jiangshangchunjiezi/article/details/73608730
iterator中hasNext()、next()、remove()源码讲解
①三个重要变量
在Itr内部定义了三个int型的变量:cursor、lastRet、expectedModCount。其中cursor表示下一个元素的索引位置,lastRet表示上一个元素的索引位置
int cursor;
int lastRet = -1;
int expectedModCount = modCount;
②hasNext()
public boolean hasNext() {
return cursor != size;
}
③next()
对于next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可。
public E next() {
checkForComodification();
int i = cursor; //记录索引位置
if (i >= size) //如果获取元素大于集合元素个数,则抛出异常
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; //cursor + 1
return (E) elementData[lastRet = i]; //lastRet + 1 且返回cursor处元素
}
④checkForComodification()
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
checkForComodification()主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过。
。modCount用于记录ArrayList集合的修改次数,初始化为0,,每当集合被修改一次(结构上面的修改,内部update不算),如add、remove等方法,modCount + 1,所以如果modCount不变,则表示集合内容没有被修改。
该机制主要是用于实现ArrayList集合的快速失败机制,在Java的集合中,较大一部分集合是存在快速失败机制的,这里就不多说,后面会讲到。
所以要保证在遍历过程中不出错误,我们就应该保证在遍历过程中不会对集合产生结构上的修改(当然remove方法除外),出现了异常错误,我们就应该认真检查程序是否出错而不是catch后不做处理。
⑤remove()
对于remove()方法的是实现,它是调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可。
这里比list.remove多的操作是expectedModCount=modCount,这也是在迭代中用iterator.remove()不报错,用list.remove()报错的原因。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
⑥List.remove(obj):找到此元素,调用fastRemove(int index)
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
fastRemove(int index):将modCount++,size--
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
对正常情况图解:
下面解释异常情况:
①
java.util.ConcurrentModificationException
出现此错处是第二次next(),modCount != expectedModCount 因为list.remove将modCount++,而没有修改expectModCount
ArrayList<String> al=new ArrayList<String>();
al.add("str1");
al.add("str2");
al.add("str3");
al.add("str4");
for (String str : al) {
al.remove(str);
}
②java.util.ConcurrentModificationException 与①类似
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
Integer integer = iterator.next();
list.remove(integer);
}
③这不是foreach形式,所以不涉及iterator
在执行list.remove(i)后,size--,并且剩余的元素组成剩余列表
for (int i = 0; i < list.size(); i++) {
list.remove(i);
}
System.out.println(list); str2、str4
二、ListIterator
专门针对List,可以双向遍历List,同时支持元素的修改
ArrayList<String> al=new ArrayList<String>();
ArrayList<String> rem=new ArrayList<String>();
al.add("001");
al.add("002");
al.add("003");
al.add("004");
ListIterator li= al.listIterator();
li.next();
while(li.hasPrevious())//能双向遍历
{
System.out.println(li.previous()+"---");
}
关于iterator.remove()与list.remove()参考:
https://juejin.im/post/59ef1ab0518825619a01e00a
https://h2pl.github.io/2018/05/09/collection3/