Java8 增加了新的语言特性(例如 lambda 表达式和默认方法),为此 Java8 的类库也进行了很多改进,例如从外部迭代到内部迭代的改变。
集合类库主要依赖于外部迭代(external iteration)。Collection 实现 Iterable 接口,从而使得用户可以依次遍历集合的元素。比如我们需要把一个集合中的形状都设置成红色,那么可以这么写:
for (Shape shape : shapes) {
shape.setColor(RED);
}
这个例子演示了外部迭代:for-each 循环调用 shapes 的 iterator() 方法进行依次遍历。外部循环的代码非常直接,但它有如下问题:
(1)Java 的 for 循环是串行的,而且必须按照集合中元素的顺序进行依次处理;
(2)集合框架无法对控制流进行优化,例如通过排序、并行、短路(short-circuiting)求值以及惰性求值改善性能。
尽管有时 for-each 循环的这些特性(串行,依次)是我们所期待的,但它对改善性能造成了阻碍。实际上,我们可以使用内部迭代(internal iteration)替代外部迭代,用户把对迭代的控制权交给类库,并向类库传递迭代时所需执行的代码。下面是前例的内部迭代代码:
shapes.forEach(s -> s.setColor(RED));
尽管看起来只是一个小小的语法改动,但是它们的实际差别非常巨大。用户把对操作的控制权交还给类库,从而允许类库进行各种各样的优化(例如乱序执行、惰性求值和并行等等)。总的来说,内部迭代使得外部迭代中不可能实现的优化成为可能。
如上所示:外部迭代同时承担了做什么(把形状设为红色)和怎么做(得到 Iterator 实例然后依次遍历)两项职责,而内部迭代只负责做什么,而把怎么做留给类库。通过这样的职责发生了转变:用户的代码会变得更加清晰,而类库则可以进行各种优化,从而使所有用户都从中受益。
从一个案例开始:遍历一个集合
外部迭代
最传统的方法是用Iterator,当然还以用for i、增强for循环等等。这一类方法叫做外部迭代,意为显式地进行迭代操作,即集合中的元素访问是由一个处于集合外部的东西来控制的,在这里控制着循环的东西就是迭代器。
我们自己定义一个List,叫ContactList:电话本List
public class ContactList extends ArrayList<Integer>{}
里面存他们的编号,现在我们要都输出,可以这么做
for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
System.out.println(contactListIterator.next());
}
内部迭代
顾名思义,这种方式的遍历将在集合内部进行,我们不会显式地去控制这个循环。无需关心遍历元素的顺序,我们只需要定义对其中每一个元素进行什么样的操作。注意在这种设定下可能无法直接获取到当前元素的下标。
比如JDK8提供的最新的Collection.forEach(…)方法。
外部迭代是由用户自己在客户端中写的循环代码, 内部迭代是由Jdk在库中执行的循环代码, 即循环写的位置从外部转移到了jdk内部.
在循环A中,我们往往最关心的是循环体内的东西,即,这个循环到底是对s做了什么, 但是在外部迭代中, 我们隐式的规定了很多,比如说,只能从第一个元素开始循环, 但其实很多场景下,是不是从第一个开始循环对我们的业务影响并不大. 而在循环B和C中, 我们只需要告诉这个集合,需要对元素s做什么,这就是内部迭代和外部迭代的第二个不同: 内部迭代只需要告诉集合,我们需要对里面的元素做什么处理