(三十七)什么是迭代器和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/

 

posted @ 2019-02-26 18:18  测试开发分享站  阅读(551)  评论(0编辑  收藏  举报