助力软件开发企业降本增效 PHP / java源码系统,只需一次付费,代码终身使用! 广告
## [迭代器Iterators](https://lingcoder.gitee.io/onjava8/#/book/12-Collections?id=%e8%bf%ad%e4%bb%a3%e5%99%a8iterators) 在任何集合中,都必须有某种方式可以插入元素并再次获取它们。毕竟,保存事物是集合最基本的工作。对于**List**,`add()`是插入元素的一种方式,`get()`是获取元素的一种方式。 如果从更高层次的角度考虑,会发现这里有个缺点:要使用集合,必须对集合的确切类型编程。这一开始可能看起来不是很糟糕,但是考虑下面的情况:如果原本是对**List**编码的,但是后来发现如果能够将相同的代码应用于**Set**会更方便,此时应该怎么做?或者假设想从一开始就编写一段通用代码,它不知道或不关心它正在使用什么类型的集合,因此它可以用于不同类型的集合,那么如何才能不重写代码就可以应用于不同类型的集合? *迭代器*(也是一种设计模式)的概念实现了这种抽象。迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构。另外,迭代器通常被称为*轻量级对象*(lightweight object):创建它的代价小。因此,经常可以看到一些对迭代器有些奇怪的约束。例如,Java 的**Iterator**只能单向移动。这个**Iterator**只能用来: 1. 使用`iterator()`方法要求集合返回一个**Iterator**。**Iterator**将准备好返回序列中的第一个元素。 2. 使用`next()`方法获得序列中的下一个元素。 3. 使用`hasNext()`方法检查序列中是否还有元素。 4. 使用`remove()`方法将迭代器最近返回的那个元素删除。 为了观察它的工作方式,这里再次使用[类型信息](https://lingcoder.gitee.io/onjava8/#/)章节中的**Pet**工具: ~~~ // collections/SimpleIteration.java import typeinfo.pets.*; import java.util.*; public class SimpleIteration { public static void main(String[] args) { List<Pet> pets = Pets.list(12); Iterator<Pet> it = pets.iterator(); while(it.hasNext()) { Pet p = it.next(); System.out.print(p.id() + ":" + p + " "); } System.out.println(); // A simpler approach, when possible: for(Pet p : pets) System.out.print(p.id() + ":" + p + " "); System.out.println(); // An Iterator can also remove elements: it = pets.iterator(); for(int i = 0; i < 6; i++) { it.next(); it.remove(); } System.out.println(pets); } } /* Output: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster [Pug, Manx, Cymric, Rat, EgyptianMau, Hamster] */ ~~~ 有了**Iterator**,就不必再为集合中元素的数量操心了。这是由`hasNext()`和`next()`关心的事情。 如果只是想向前遍历**List**,并不打算修改**List**对象本身,那么使用*for-in*语法更加简洁。 **Iterator**还可以删除由`next()`生成的最后一个元素,这意味着在调用`remove()`之前必须先调用`next()`。\[^4\] 在集合中的每个对象上执行操作,这种思想十分强大,并且贯穿于本书。 现在考虑创建一个`display()`方法,它不必知晓集合的确切类型: ~~~ // collections/CrossCollectionIteration.java import typeinfo.pets.*; import java.util.*; public class CrossCollectionIteration { public static void display(Iterator<Pet> it) { while(it.hasNext()) { Pet p = it.next(); System.out.print(p.id() + ":" + p + " "); } System.out.println(); } public static void main(String[] args) { List<Pet> pets = Pets.list(8); LinkedList<Pet> petsLL = new LinkedList<>(pets); HashSet<Pet> petsHS = new HashSet<>(pets); TreeSet<Pet> petsTS = new TreeSet<>(pets); display(pets.iterator()); display(petsLL.iterator()); display(petsHS.iterator()); display(petsTS.iterator()); } } /* Output: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 5:Cymric 2:Cymric 7:Manx 1:Manx 3:Mutt 6:Pug 4:Pug 0:Rat */ ~~~ `display()`方法不包含任何有关它所遍历的序列的类型信息。这也展示了**Iterator**的真正威力:能够将遍历序列的操作与该序列的底层结构分离。出于这个原因,我们有时会说:迭代器统一了对集合的访问方式。 我们可以使用**Iterable**接口生成上一个示例的更简洁版本,该接口描述了“可以产生**Iterator**的任何东西”: ~~~ // collections/CrossCollectionIteration2.java import typeinfo.pets.*; import java.util.*; public class CrossCollectionIteration2 { public static void display(Iterable<Pet> ip) { Iterator<Pet> it = ip.iterator(); while(it.hasNext()) { Pet p = it.next(); System.out.print(p.id() + ":" + p + " "); } System.out.println(); } public static void main(String[] args) { List<Pet> pets = Pets.list(8); LinkedList<Pet> petsLL = new LinkedList<>(pets); HashSet<Pet> petsHS = new HashSet<>(pets); TreeSet<Pet> petsTS = new TreeSet<>(pets); display(pets); display(petsLL); display(petsHS); display(petsTS); } } /* Output: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 5:Cymric 2:Cymric 7:Manx 1:Manx 3:Mutt 6:Pug 4:Pug 0:Rat */ ~~~ 这里所有的类都是**Iterable**,所以现在对`display()`的调用显然更简单。