Iterable和Iterator


文章目录

  • Iterable和Iterator
  • 前言
  • 分析源码
  • 自定义迭代器示例
  • 总结


前言

1.Iterable:是一个接口,仅有一个方法的声明,即 Iterator iterator(),用于获取迭代器。实现了该接口的类可以使用foreach来遍历。该方法必须返回一个迭代器,而迭代器类通常作为内部类来实现,此内部类必须实现Iterator接口。

2.Iterator:是一个接口,内部声明了三个方法:boolean hasNext(); E next(); void remove();该接口的实现类一般作为集合类的内部类。

3.iterator:返回迭代器的方法

4.foreach:对集合进行遍历,例如for(Integer i : myCollection) : 会自动调用myCollection的iterator方法获得迭代器。因此要使用foreach的类必须实现Iterable接口,实现iterator方法,方法中返回Iterator对象【该对象所属类必须实现Itertor接口,并实现hasNext next 和remove三个方法】。

5.JDK提供的实现Collection接口的类,均实现了itertor方法,均可以使用foreach。

分析源码

Iterator与Iterable
iterator为Java中的迭代器对象,是能够对List这样的集合进行迭代遍历的底层依赖。而iterable接口里定义了返回iterator的方法,相当于对iterator的封装,同时实现了iterable接口的类可以支持for each循环。

iterator内部细节
jdk中Iterator接口主要方法如下:

public interface Iterator<E> {
	boolean hasNext();
  	E next();
}

iterator通过以上两个方法定义了对集合迭代访问的方法,而具体的实现方式依赖于不同的实现类,具体的集合类实现Iterator接口中的方法以实现迭代。

可以发现,在List中并没有实现Iterator接口,而是实现的Iterable接口。进一步观察Iterable接口的源码可以发现其只是返回了一个Iterator对象。

public interface Iterable<T> {
  Iterator<T> iterator();
}

所以我们可以使用如下方式来对List进行迭代了(通过调用iterator()方法,而这个方法可以有实现类灵活实现,不同的实现类均可自定义遍历方式实现)

Iterator it = list.iterator();
while (it.hasNext()) {
    System.out.print(it.next() + ",");
}

同时实现了Iterable接口的还可以使用for each循环,参考foreach原理既可以知道。

自定义迭代器示例

创建一个类实现Iterable接口:

package com.cmb.dtpframework.exception;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
public class EnumErrorCodeProvider implements Iterable<IErrorCode>{    
	private Map<String, IErrorCode> errorCodes; 
	   
	public IErrorCode getErrorCode(String code) {       
 		return errorCodes.get(code);   
 	}   
 	
   	@Override   
   	public Iterator<IErrorCode> iterator() {      
    	return errorCodes.values().iterator(); 
    }
}

使用foreach遍历:

private void loadFromErrorCodeProviders(Collection<ErrorCodeProvider> errorCodeProviders, Map<String, IErrorCode> errorCodes) {   
 for (ErrorCodeProvider errorCodeProvider : errorCodeProviders) {     
    for (IErrorCode errorCode : errorCodeProvider) {     
           String code = errorCode.getCode();        
           IErrorCode previousErrorCode = errorCodes.putIfAbsent(code, errorCode);            
           if (previousErrorCode != null) {              
	           // 错误码重复定义警告               
	           FrameworkException e = new FrameworkException(ErrorConst.ZZ2E011, code, previousErrorCode.getClass(), errorCode.getClass());               
                e.printStackTrace();         
            }    
     }  
  }
}

查看jdk编译后的class文件:

private void loadFromErrorCodeProviders(Collection<ErrorCodeProvider> errorCodeProviders, Map<String, IErrorCode> errorCodes) {    
	Iterator var3 = errorCodeProviders.iterator();   
	while(var3.hasNext()) {       
 		ErrorCodeProvider errorCodeProvider = (ErrorCodeProvider)var3.next();  
        Iterator var5 = errorCodeProvider.iterator();    
        while(var5.hasNext()) {            
            IErrorCode errorCode = (IErrorCode)var5.next();           
            String code = errorCode.getCode();           
            IErrorCode previousErrorCode = (IErrorCode)errorCodes.putIfAbsent(code, errorCode);      
            if (previousErrorCode != null) {              
            	FrameworkException e = new FrameworkException(ErrorConst.ZZ2E011, new Object[]{code, previousErrorCode.getClass(), errorCode.getClass()});  
                e.printStackTrace();      
             }     
        } 
    }
}

由于重写的iterator()方法返回的是map的迭代器,因此编译后代码中的var3.hasNext()(等价于errorCodes.hasNext())判断的是errorCodes是否有下一个值(可以参考map实现hasNext的源码),var5.next()(等价于errorCodes.next())

理解这里的核心点:
这部分代码
for (IErrorCode errorCode : errorCodeProvider) {
会被jdk编译成
Iterator var5 = errorCodeProvider.iterator();while(var5.hasNext()) { IErrorCode errorCode = (IErrorCode)var5.next();

总结

1、for()循环是最快的遍历方式,随后是iterator()迭代器,最后是foreach循环

2、foreach语法糖,内部实现是迭代器(jdk会编译成迭代器)
ArrayList底层是数组,所以使用下标访问更快;而LinkedList是链表,用get每次都要遍历,所以用foreach更好;
foreach中对collection进行遍历的时候,就不能使用collection.remove(),会报ConcurrentModificationException异常(在next方法中首先调用了checkForComodification方法,该方法会判断modCount是否等于expectedModCount);

3、有些集合类可能不止一种遍历方式,实现了Iterable的类可以再实现多个Iterator内部类
例如LinkedList中的ListItr和DescendingIterator两个内部类,就分别实现了双向遍历和逆序遍历。通过返回不同的Iterator实现不同的遍历方式,这样更加灵活

4、习惯用法
for、foreach循环、iterator迭代器都是我们常用的一种遍历方式,你可以用它来遍历任何东西:包括数组、集合等


留言:文章是博主本人遇到问题,通过各种渠道收集资料解决后,并记录在word文档里,最后才整理发博,因此有些相似的内容是借鉴网友的,若有雷同,请谅解!