本文摘抄至 : chenssy、龙盛国际、benjaminwhx
摘要 : 迭代对于我们搞Java的来说绝对不陌生。我们常常使用JDK提供的迭代接口进行Java集合的迭代。在使用java集合的时候,都需要使用Iterator。但是java集合中还有一个迭代器ListIterator,在使用List、ArrayList、LinkedList和Vector的时候可以使用。这两种迭代器有什么区别呢?
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Stirng string = iterator.next();
//do something
}
代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类,它是一个很典型的设计模式。Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。 在没有迭代器时我们都是这么进行处理的。 如下: 对于数组我们是使用下标来进行处理的:
int[] arrays = new int[10];
for(int i = 0 ; i < arrays.length ; i++){
int a = arrays[i];
//do something
}
对于ArrayList是这么处理的:
List list = new ArrayList();
for(int i = 0 ; i < list.size() ; i++){
String string = list.get(i);
//do something
}
对于这两种方式,我们总是要事先知道集合的内部结构,访问代码和集合本身是紧密耦合。无法将访问逻辑从集合类和客户代码中分离出来。同时每一种集合对应一种遍历的方法。客户端代码无法复用。在实际应用中如何需要将上面两个集合进行整合是相当麻烦的。所以为了解决以上问题,Iterator模式腾空出世,它总是用同一种逻辑来遍历集合。使得客户端自身不需要来维护集合的内部结构。所有的内部结构都由Iterator来维护。客户端从不直接和集合类打交道,它总是控制Iterator,向他发送“向前”、“向后”、“取当前元素”的命令,就可以间接遍历整个集合。
以上是对Iterator模式进行简单的说明。下面我们看看Java中Iterator接口,看它是如何来进行实现的。
public interface Iterator{
boolean hasNext() ;
Object next() ;
void remove() ;
}
其中 :
Object next() : 返回迭代器刚越过的元素的引用,返回值是Object。需要强制转换成自己需要的类型
boolean hasNext() : 判断容器内是否还有可供访问的元素。
void remove() : 删除迭代器刚越过的元素 对于我们而言,我们一般只需要使用
next()、
hasNext()两个方法即可完成迭代。如下:
for(Iterator it = c.iterator;it.hasNext(); ){
Object o = it.next();
//do something
}
前面阐述了
Iterator有一个很大的优点就是我们不必知道集合的内部结果、集合的内部结构、状态由
Iterator来维持。通过统一的方法
hasNext()、
next()来判断。获取下一个元素。至于具体的内部实现我们就不必关心了。
但是作为一个合格的程序员我们非常有必要来弄清楚Iterator的实现。下面就是ArrayList的源码进行分析。
下面就ArrayList的Iterator实现来分析,其实如果我们理解了ArrayList、Hashset、TreeSet的数据结构,内部实现,对于他们是如何实现Iterator也会胸有成竹的。因为ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引即可,其方法的实现比较简单
private class Itr implements Iterator<E> {
//do something
}
而ArrayList的iterator()方法实现:
public Iterator iterator() {
return new Itr();
}
所以通过使用
ArrayList.iterator()方法返回的是
Itr()内部类。所以在我们需要关心的就是
Itr()内部类的实现 : 在
Itr()内部定义三个
int型的变量 :
cursor、
lastRet、
expectedModCount。 其中
cursor表示下一个元素的索引位置,
lastRet表示上一个元素的索引位置。
int cursor;
int lastRet = -1 ;
int expectedModCount = modCount ;
从
cursor、
lastRet一直比
cursor少一个。所以
hasNext()实现方法异常简单。只需要判断
cursor和
lastRet是否相等即可。
public boolean hasNext() {
return cursor != size;
}
对于
next()实现其实也是比较简单的。只要返回
cursor索引位置的元素即可。然后修改
cursor、
lastRet即可。
public E next(){
checkForComodification();
//记录索引位置
int i = cursor ;
//如果获取元素大于集合元素个数,则抛出异常
if(i >= size)
throw new ConcurrentModificationException();
//cursor + 1
cursor = i + 1 ;
//lasrRet + 1 且返回cursor处元素
return (E) elementData[lastRet = i];
}
checkForComodification() 主要用来判断集合的修改次数是否合法。即用来判断遍历过程中集合是否被修改。在文章** ArrayList**中已经阐述。
modCount 用于记录
ArrayList 集合的修改次数。初始化为0,,每当集合被修改一次(结构上面的修改,内部
updata不算 ),如
add 、
remove 等方法,
modCount + 1 ,所以如果
modCount 不变,则表示集合内容没有被修改。该机制主要是用于实现
ArrayList 集合的快速失败机制。在Java的集合中,较大一部分集合是存在快速失败机制的。这里就不多说,后面会讲到。所以要保证在遍历过程中不出错误,我们就应该保证在遍历过程中不会对集合产生结构上的修改(当然
remove方法除外),出现了异常错误,我们就应该认真检查程序是否出错而不是
catch后不做处理。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
对于
remove()方法的是实现,它是调用
ArrayList本身的
remove()方法删除lastRet位置元素,然后修改
modCount即可。
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();
}
}
这里就对
ArrayList的
Iterator实现讲解到这里。
在使用java集合的时候,都需要使用Iterator。但是java集合中还有一个迭代器ListIterator,在使用List、ArrayList、LinkedList和Vector的时候可以使用。这两种迭代器(两种迭代器有时候是不能通用的)有什么区别呢?下面我们详细分析。这里有一点需要明确的时候,迭代器指向的位置是元素之前的位置,如下图所示:
这里假设集合List由四个元素List1、List2、List3和List4组成,当使用语句Iterator it = List.Iterator()时,迭代器it指向的位置是上图中Iterator1指向的位置,当执行语句it.next()之后,迭代器指向的位置后移到上图Iterator2所指向的位置。
首先看一下Iterator和ListIterator迭代器的方法有哪些。
Iterator迭代器包含的方法有:
hasNext():如果迭代器指向位置后面还有元素,则返回 true,否则返回false
next():返回集合中Iterator指向位置后面的元素
remove():删除集合中Iterator指向位置后面的元素
ListIterator迭代器包含的方法有:
add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前
hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false
hasPrevious() :如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false
next():返回列表中ListIterator指向位置后面的元素
nextIndex() :返回列表中ListIterator所需位置后面元素的索引
previous() :返回列表中ListIterator指向位置前面的元素
previousIndex():返回列表中ListIterator所需位置前面元素的索引
remove():从列表中删除next()或previous()返回的最后一个元素(有点拗口,意思就是对迭代器使用hasNext()方法时,删除ListIterator指向位置后面的元素;当对迭代器使用hasPrevious()方法时,删除ListIterator指向位置前面的元素)
set(E e):从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e
都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。
1.使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。
2.ListIterator有add方法,可以向List中添加对象,而Iterator不能。
3.ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。
/** hu*/
public class listergodic {
public static void main(String[] args) {
List a = new ArrayList<>() ;
List b = new ArrayList<>() ;
a.add("A") ;
a.add("B") ;
a.add("C") ;
b.add("1") ;
b.add("2") ;
b.add("3") ;
b.add("4") ;
b.add("5") ;
ListIterator aIt = a.listIterator();
ListIterator bIt = b.listIterator();
while(bIt.hasNext()){
while(aIt.hasNext()){
aIt.next();
}
aIt.add(bIt.next());
}
System.out.println(a);
while(aIt.hasPrevious()){
System.out.println(aIt.previous()+"***");
}
}
4.ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
5.都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。
/** hu*/
public class listergodic {
public static void main(String[] args) {
List a = new ArrayList<>() ;
List b = new ArrayList<>() ;
a.add("A") ;
a.add("B") ;
a.add("C") ;
b.add("1") ;
b.add("2") ;
b.add("3") ;
b.add("4") ;
b.add("5") ;
ListIterator aIt = a.listIterator();
ListIterator bIt = b.listIterator();
while(aIt.hasNext()){
System.out.print(aIt.nextIndex()+"***, ");
System.out.print(aIt.next()+"***, ");
}
System.out.println();
//逆向遍历为 true
while(aIt.hasPrevious()){
//返回上一个索引
System.out.print(aIt.previousIndex()+"***, ");
//返回上一个索引对应的值
System.out.print(aIt.previous()+"***, ");
}
System.out.println();
}
/** hu*/
public class listergodic {
public static void main(String[] args) {
List a = new ArrayList<>() ;
List b = new ArrayList<>() ;
a.add("A") ;
a.add("B") ;
a.add("C") ;
b.add("1") ;
b.add("2") ;
b.add("3") ;
b.add("4") ;
b.add("5") ;
ListIterator aIt = a.listIterator();
ListIterator bIt = b.listIterator();
//删除第2个Lsit中为偶数的所有元素
while(bIt.hasNext()){
bIt.next();
if(bIt.hasNext()){
bIt.next();
bIt.remove();
}
}
System.out.println(b);
}