Iterator、Iterable接口

Java所有集合类的基本接口是Collection接口。而Collection接口继承java.lang.Iterable接口。

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

Iterator

java.util.Iterator

子接口: ListIterator<E>,PrimitiveIterator<T,T_CONS>,PrimitiveIterator.OfDouble,PrimitiveIterator.OfInt,PrimitiveIterator.OfLong,XMLEventReader
实现类: BeanContextSupport.BCSIterator,EventReaderDelegate,Scanner

在这里插入图片描述

List<Integer> list=new ArrayList<>();
list.add(5);
list.add(23);
list.add(42);
for(int i=0;i<list.size();i++){
	System.out.print(list.get(i)+",");
}
Iterator it=list.iterator();
while(it.hasNext()){
	System.out.print(it.next()+",");
}

for(Interger i:list){
	System.out.print(i+",");
}

第一种就是普通的for循环,第二种为迭代器遍历,第三种是for each循环。后面两种方式涉及到Java中的Iterator和Iterator对象。

Iterator是遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构

Iterator主要用于遍历(迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器。

  • Object next(): 一开始迭代器在所有元素的左边,在调用next()之后,迭代器移到第一个和第二个元素之间,next()方法会回迭代器刚刚经过的元素
  • boolean hasNext(): hasNext()若返回True,则表明接下来还有元素,迭代器不再尾部。
  • void remove(): remove()方法必须和next方法一起使用,功能是去除刚刚next方法返回的元素
  • void forEachRemaining(Consumer action): 该方法可以使用Lambda表达式来遍历集合元素
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class ForEachDemo{
	public static void main(string args){
		Collection<String> a=new ArrayList<String>();
		a.add("Bob");
		a.add("Alice");
		a.add("Lisy");
		Iterator<String> iterator=a.iterator();
		while(iterator.hasNext()){
			String ele=iterator.next();
			System.out.println(ele);     //Bob  Alice  Lisy
		}
		System.out.println(a);      //[Bob,Alice,Lisy]
		iterator=a.iterator();       //使得迭代器重新在所有元素的最左边
		iterator.next();
		iterator.remove();
		System.out.println(a);     //[Alice,Lisy]
	}
}

结果:
在这里插入图片描述

当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给迭代变量,而是把集合元素的值传给了迭代变量,所以**修改迭代变量的值对集合元素本身没有任何影响。**故当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变。但是可以通过Iterator的remove()方法删除上一个next()方法返回的集合元素

Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构

例如,如果没有使用Iterator,遍历一个数组的方法是使用索引:

for(int i=0;i<array.size();i++){
	.....
	get(i);
	.......
}

而访问一个链表(LinkedList)又必须使用while循环:

while((e=e.next())!=null){
	.....
	e.data()
	......
}

以上两种方法客户端都必须事先知道集合的内部结构,访问代码和集合本身是紧耦合,无法将访问逻辑从集合类和客户端代码中分离出来。由于每一种集合对应一种遍历方法,这会导致客户端代码无法复用,例如以后如果需要把ArrayList更换为LinkedList,则原来的客户端代码必须全部重写。

为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合:

for(Iterator it=c.iterater();it.hasNext();){
	......
}

奥秘就在于客户端自身不维护遍历集合的"指针",所有的内部状态(如当前元素位置,是否有下一个元素)都有Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。这样客户端就不用直接和集合类打交道,只要控制Iterator,向它放松"向前",“向后”,"取当前元素"的命令,就可以遍历整个集合。

为什么一定要去实现Iterable这个接口呢?为什么不直接实现Iterator接口呢?
看一下JDK中的集合类,比如List一族或者Set一族,都是继承了Iterable接口,但并不直接继承Iterator接口。
仔细想一下这么做是有道理的,因为Iterator接口的核心方法next()或者hasNext()是依赖于迭代器的当前迭代位置的。如果Collection直接继承Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。
除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。,用来重置当前迭代位置。但即使这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。多个迭代器时互不干扰的。

Iterable

java.lang.Iterable

子接口:BeanContext, BeanContextServices, BlockingDeque<E>, BlockingQueue<E>, Collection<E>, Deque<E>, DirectoryStream<T>, List<E>, NavigableSet<E>, Path, Queue<E>, SecureDirectoryStream<T>, Set<E>, SortedSet<E>, TransferQueue<E>

实现类:AbstractCollection, AbstractList, AbstractQueue, AbstractSequentialList, AbstractSet, ArrayBlockingQueue, ArrayDeque, ArrayList, AttributeList, BatchUpdateException, BeanContextServicesSupport, BeanContextSupport, ConcurrentHashMap.KeySetView, ConcurrentLinkedDeque, ConcurrentLinkedQueue, ConcurrentSkipListSet, CopyOnWriteArrayList, CopyOnWriteArraySet, DataTruncation, DelayQueue, EnumSet, HashSet, JobStateReasons, LinkedBlockingDeque, LinkedBlockingQueue, LinkedHashSet, LinkedList, LinkedTransferQueue, PriorityBlockingQueue, PriorityQueue, RoleList, RoleUnresolvedList, RowSetWarning, SerialException, ServiceLoader, SQLClientInfoException, SQLDataException, SQLException, SQLFeatureNotSupportedException, SQLIntegrityConstraintViolationException, SQLInvalidAuthorizationSpecException, SQLNonTransientConnectionException, SQLNonTransientException, SQLRecoverableException, SQLSyntaxErrorException, SQLTimeoutException, SQLTransactionRollbackException, SQLTransientConnectionException, SQLTransientException, SQLWarning, Stack, SyncFactoryException, SynchronousQueue, SyncProviderException, TreeSet, Vector

Iterable接口只有一个方法(默认方法除外),就是iterator()方法,返回集合的Iterator对象。所有实现Iterable接口的集合还可以使用foreach循环进行遍历
Iterable的源码如下:

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

Iterator与Iterable的区别

从上面的介绍可以看到Iterator接口是提供了一种统一的遍历集合元素的方式。使用Iterator对象可以不同关心具体集合对象的具体类型和内部实现,统一使用Iterator对象的接口方法就可以遍历集合。
而Iterable接口里不仅定义了返回Iterator的方法,相当于对Iterator的封装,同时实现了Iterable接口的类可以支持for each循环。

有一个问题,为什么不直接将hashNext(),next()方法放在Iterable接口中,其他类直接实现就可以了?
原因是有些集合类可能不止一种遍历方式,实现了Iterable的类可以再多实现多个Iterator内部类。通过返回不同的Iterator实现不同的遍历方式,这样更加灵活。如果把两个接口合并,就没法返回不同的Iterator实现类了。

猜你喜欢

转载自blog.csdn.net/zjpp2580369/article/details/83275222