Iterator和ListIterator

Iterator和ListIterator都是Java集合框架中的迭代器。

1.java.util interface Iterator<E>

Iterator接口有3个方法。

  boolean hasNext()

  E next()

  void remove()

  

  hasNext方法在迭代器还有元素可以迭代时,返回true。否则,返回false。

  next方法就具体返回下一个元素,将迭代器里的指针(光标)向后移动1次。每使用一次,指针就向后移动一次。如果没有下一个元素了,就返回异NoSuchElementException。  

//	java.lang.Exception
//		java.lang.RuntimeException
//			java.util.NoSuchElementException 

      hasNext方法返回boolean,所以没有元素迭代时,返回false;next方法返回对象,所以没有下一个元素时,直接返回了异常(异常也是对象)。

 

例子1

ArrayList<String> al=new ArrayList<String>();
		al.add("a");
		al.add("c");
		al.add("b");
		al.add("d");
		
		Iterator<String> i=al.iterator();
		while(i.hasNext()){//hasNext只是判断,并不移动指针
			System.out.println("有下一个迭代元素吗?"+i.hasNext());
			System.out.println(i.next());//返回元素,指针向后移动一次
		}
		System.out.println("这时还有下一个元素可以迭代吗?"+i.hasNext());

运行结果

  有下一个迭代元素吗?true
  a
  有下一个迭代元素吗?true
  c
  有下一个迭代元素吗?true
  b
  有下一个迭代元素吗?true
  d
  这时还有下一个元素可以迭代吗?false

 

例子2

//前5行与上一个例子相同,略
		Iterator<String> i=al.iterator();
		while(i.hasNext()){
			i.next();
			i.next();
			i.next();
			System.out.println(i.next());
		}
		System.out.println(al);

运行结果

  d
  [a, c, b, d]

 

例子3

//前5行与上一个例子相同,略
		Iterator<String> i=al.iterator();
		while(i.hasNext()){
			i.next();
			i.next();
			i.next();	
			i.next();//使用了4次next方法,这时还没问题
			System.out.println("4");
			i.next();//没有元素可以迭代了
			System.out.println("5");
		}

运行结果

  4
  Exception in thread "main" java.util.NoSuchElementException
    at java.util.AbstractList$Itr.next(AbstractList.java:350)
    at com.zcc.MyIterator.test2(MyIterator.java:44)
    at com.zcc.MyIterator.main(MyIterator.java:9)

 

  remove方法移除迭代器返回的最后一个元素,即最后一个next方法返回的元素。

  注意:

  1.一个next之后只能使用一次remove方法,因为你总不能将一个元素移除多次吧。否则,会抛出IllegalStateException异常。

  2.在进行迭代时,不能使用其他方法(如集合自己的方法)对集合进行操作(如remove,add)。没法对对象进行并发修改。否则,会抛出ConcurrentModificationException异常。

 

//	java.lang.Exception
//		java.lang.RuntimeException
//			java.util.IllegalStateException
//			java.util.ConcurrentModificationException

  

例子4

//前5行与上一个例子相同,略
		Iterator<String> i=al.iterator();
		while(i.hasNext()){
			System.out.println(i.next());
			i.remove();//这时已经将next返回的元素删除了
		}
		System.out.println(al);

运行结果  

  a
  c
  b
  d
  []

 

例子5

//前5行与上一个例子相同,略
		Iterator<String> i=al.iterator();
		while(i.hasNext()){
			//i.remove(); 未调用next方法,不知道要删谁。抛出IllegalStateException
			System.out.println(i.next());
			i.remove();
			//i.remove(); 对一个next方法连续使用了2个remove,元素已经被删除了。抛出IllegalStateException
			//al.add("z");并发操作 抛出ConcurrentModificationException		
		} 
		System.out.println(al);

  

2.java.util interface ListIterator<E> extends Iterator<E>

ListIterator是Iterator的加强版,专门为List子类准备的迭代器。在Iterator的基础上又增加了6个方法,一共有9个方法。

这样就可以在迭代器里对集合不止进行删除操作了,还可以修改与增加,并且不会产生并发操作异常。

boolean hasNext()

boolean hasPrevious()

E    next()

E    previous()

int   nextIndex()

int   previous()

void   add(E e)

void   remove()

void   set(E e)

与Iterator相比,ListIterator可以向前遍历,得到元素的索引,增加和修改元素。

在ListIterator里,迭代器的光标位于元素之间。集合有n个元素,则光标有n+1个位置。

   Element(0)  Element(1)  Element(2)  Element(3) ... Element(n-1)
^       ^       ^        ^       ^        ^

 

只要光标不在最后的位置上,hasNext方法都返回true。

只要光标不在最前的位置上,hasPrevious方法都返回true。

nextIndex方法返回光标后面一个元素的索引(0~n-1)。如果光标在最后,返回列表的大小。

previousIndex方法返回光标前一个元素的索引(0~n-1)。如果光标在最前,返回-1。

上面的四个方法都不移动光标位置,且返回值人畜无害(int和boolean),都不会产生异常。

 

next方法返回光标的下一个元素,并将光标向后移1位。如果光标已经在最后了,抛出NoSuchElementException异常。

previous方法返回光标前面的一个元素,并将光标向前移1位。如果光标已经在最前了,抛出NoSuchElementException异常。

ListIterator的另外三个方法在使用时,都和next/previous方法相关。

remove方法移除一个元素,因为迭代器里光标并不对应着元素的下标,而是在元素之间,所以如果你要用remove方法删除一个元素时,集合怎么知道你要删除哪一个元素呢?是光标前面一个元素还是后面一个元素

所以在使用remove方法之前,要使用next或previous方法,"定位"住你要删除的元素。

  Element(3)  Element(4)

^

使用了next方法后,

 Element(3)  Element(4)

      ^

这时再使用remove方法,迭代器就知道,你要删除Element(3)这个元素了。

关于remove方法的注意事项

  1.既没有调用next方法,也没有调用previous方法,就使用remove方法,会抛出异常IllegalStateException。

  2.在每个next/previous方法后,只能使用一个remove方法。因为被"定位"的元素已经被删除了,无法再删了。否则的话,抛出异常IllegalStateException。

  3.在ListIterator里,返回值为空的方法和返回对象的方法一样,都可能抛出异常。返回值为int/boolean的方法不会。

  //add

set方法替换元素,和remove方法类似,都需要next/previous方法来对元素进行"定位"后才能使用。但和remove方法相比,一次next/previous后可以调用多次set,因为你可以对同一个元素进行多次修改,反正最终的结果是由最后一次set决定的。

set和remove方法混合使用时,不能在使用了remove之后再使用set(你都删除了,还怎么修改,否则IllegalStateException等着你)。但是可以在set之后使用remove(但这样你做的修改就前功尽弃了,因为元素被删掉了)。

add方法将元素插入列表,元素被插到光标的前面。准确点来说,被插入的元素在插入后,位置就在光标后面一个元素的前面。如果光标前有元素的话,插入的元素就插在光标前一个元素与光标之间。

例子6

ListIterator<String> i=al.listIterator();
		//一开始光标的默认位置在Element(0)前
		i.add("1");
		i.add("2");
		System.out.println(al);

运行结果

  [1, 2, a, c, b, d]

^a b c d  => 1^a b c d  => 1 2^a b c d

这结果也符合常理,插入元素的话,后插的元素肯定在先插元素的后面吧。

当然,也可以在next/previous之后使用add,使用次数和set一样,可以使用多次。next/previous这次并不是对元素定位,因为add并不像remove和set一样对元素操作,next/previous操作只是移动光标位置,调整add要插入的位置。

在add对之后调用的next不会产生影响,如果之后调用previous,就会返回最后一个add加入的元素。

例子7

ListIterator<String> i=al.listIterator();
		i.next();
		i.next();
		i.add("1");
		i.add("2");
		System.out.println(i.previous());
		System.out.println(al);  

运行结果

  2

  [a, c, 1, 2, b, d]

解释

  先用两次next移动光标。 a c^b d。

  调用两次add   a c 1^b d => a c 1 2^b d   插入位置在光标前一个元素与光标之间。

 

add方法与remove方法,set方法一起使用时的注意事项

  1.在next/previous后,使用了add就不能在用remove和set了。因为这时你"定位"的元素就从1个变成了多个。

  ^a b c  next  a^b c 定位了a

  a^b c  add(z)  a z^b c 你好像定位了a和z

  a z^b c remove/set IllegalStateException

所以可以总结一下,使用了next/previous之后,在使用remove/set前使用了add/remove方法就会IllegalStateException。 定位出错。

在使用remove/set前没有使用next/previous也会IllegalStateException。 没定位。

 

posted @ 2017-05-17 10:03  EdwardChu  阅读(221)  评论(0编辑  收藏  举报