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文档里,最后才整理发博,因此有些相似的内容是借鉴网友的,若有雷同,请谅解!