Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
(提供一种方法顺序访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。)
假设我们需要提供一个接收集合参数的方法,在方法中会对这个集合中的内容进行遍历操作,拿我们常用的List来示意,我们一般可以这样遍历
/**
* 操作集合的方法
* @param list
*/
public void operation(List<String> list){
for(int i=0; i<list.size(); i++){
System.out.println(list.get(i));
}
// do something
}
一般情况下,我们都是使用简单列表存储元素。 但有些集合还会使用栈、 树、 图和其他复杂的数据结构。如果我们这个方法,想要接收处理这么多集合类型的数据,该怎么处理呢。
很多人都会想到,我们可以将方法参数类型改为Collection,这样可以接收很多类型的集合数据。那么,我们来改下试试。
Collection接口中没有get(i)这个方法~,这就尴尬了,那咋整,哎,有人会说,我们可以用增强for循环啊!
嗯,是的,增强for循环是可以解决这个遍历的问题,代码示意如下
/**
* 操作集合的方法
* @param collection
*/
public void operation(Collection<String> collection){
for(String str : collection){
System.out.println(str);
}
// do something
}
增强for循环之所以能实现遍历是有原因的,原理我们放到后边再说,这里先跟大家展示下通过迭代器的方式来进行遍历。
/**
* 操作集合的方法
* @param collection
*/
public void operation(Collection<String> collection){
// 创建迭代器对象
Iterator<String> iterator = collection.iterator();
// 通过迭代器对象,遍历集合中的元素
while(iterator.hasNext()){
System.out.println(iterator.next());
}
// do something
}
使用迭代器也很容易的解决了我们所遇到的问题。
以ArrayList为例,我们看看迭代器的原理是怎样的。
ArrayList - -> List 一> Collection 一> Iterable
ArrayLisy类实现了List接口,List接口继承Collection接口,Collection接口继承Iterable接口,在Iterable接口中,声明了一个创建迭代器的方法iterator()
public interface Iterable<T> {
/**
* 创建迭代器的方法声明
*/
Iterator<T> iterator();
}
ArrayLisy 中最终实现 iterator() 方法,返回一个新创建的Itr对象。
public Iterator<E> iterator() {
return new Itr();
}
Itr 是 ArrayLisy 类中定义的,实现了 Iterator 接口的内部类 。Itr 类中实现了 hasNext() 方法和 next() 方法的具体业务逻辑。
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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;
return (E) elementData[lastRet = i];
}
...
}
hasNext() 方法判断是否有下一个元素,如果有,通过next() 方法取出,这样就实现了对 ArrayLisy 集合中元素的遍历操作。
如果我们使用的是 HashSet 而不是 ArrayLisy ,原理其实是一样的。
前面,我们提到可以通过增强for循环对集合进行遍历,这里我们剖析一下,这种增强for循环底层是如何实现的。
我们对如下代码进行反编译
/**
* 操作集合的方法
* @param collection
*/
public void operation(Collection<String> collection){
for(String str : collection){
System.out.println(str);
}
// do something
}
反编译后
public void operation(Collection<String> collection) {
Iterator var2 = collection.iterator();
while(var2.hasNext()) {
String str = (String)var2.next();
System.out.println(str);
}
}
通过反编译,我们看到,增强for循环遍历集合是通过迭代器模式来实现的。
除了集合,增强for循环还可以遍历数组,数组中是没办法使用迭代器模式的,那遍历数组的增强for循环又是怎么实现的呢?
/**
* 操作集合的方法
* @param Strs
*/
public void operation(String[] Strs){
for(String str : Strs){
System.out.println(str);
}
// do something
}
反编译后
public void operation(String[] Strs) {
String[] var2 = Strs;
int var3 = Strs.length;
for(int var4 = 0; var4 < var3; ++var4) {
String str = var2[var4];
System.out.println(str);
}
}
反编译后,我们其实可以看到,通过增强for循环对数组进行遍历,其实就是使用了最普通的索引下标的遍历方式。
综上,归纳起来,增强for循环原理就这两点:
优点
缺点
java 中已经把迭代器运用到各个聚集类( collection)中了,使用 java 自带的迭代器就已经满足我们的需求了。不过了解了迭代器模式,可以帮助我们更好的理解Java的一些源码实现。