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实现类了。