本文要搞清的问题:
-
Iterator 源码如何实现遍历
-
for each语法糖 底层是什么
-
iterator和iterable的关系
一. Iterator 概述
Iterator 大家应该都很熟悉,今天我们就来扒他外衣, 探探究竟。首先来看下源码:
public interface Iterator {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
Iterator主要是用来遍历集合的,内部维护了一个int下标指针,表示当前遍历到集合的哪个位置了。
- hasNext:判断是否还有下一个元素。
- next :指针移动到下一元素,并返回当前元素。
- remove: 不允许remove,各集合实现类可重载。
- forEachRemaining : 直接执行了遍历每个元素的同时回调你传的action业务逻辑。
二. 集合是如何获取迭代器实例(请注意下面都是挑选ArrayList为例子)
请看ArrayList的Iterator 遍历用法
public static void main(String[] args) throws Exception {
ArrayList list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
// 获取ArrayList的迭代器
Iterator it = list.iterator();
while (it.hasNext()) {
System.err.println(it.next());
}
}
很显然是通过ArrayList的iterator来获取Iterator 实例的。下面看下实现
class ArrayList{
protected transient int modCount = 0;
private int size;
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator {
int cursor;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
三. Itr 里面的成员含义
- cursor : 当前迭代器遍历到集合的哪个位置了。每调next方法都会++ 。
- lastRet : 为了控制remove逻辑的一个变量,后面会说。
- expectedModCount : 保证用Iterator遍历过程中, 是不能操作ArrayList的 remove,update,add等方法的。
四. Itr 的checkForComodification方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
checkForComodification方法作用是检测集合是否被修改过。
modCount : 为ArrayList的修改次数统计,在操作ArrayList(增,删,改)时,此值会加加。
Itr 初始化的时候,会把集合的modCount 赋值给expectedModCount , 在调用next遍历的时候会先调用checkForComodification , 确保ArrayList 没有被修改过,否则抛出异常。
五. Itr 的next方法
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];
}
直接取出当前cursor位置,初始化时为0 ,调用一次next 就加加到下个位置。ArrayList.this.elementData 为 ArrayList真正的数据存储数组。方法出参返回当前cursor位置的元素。
lastRet 等于cursor减1 [ 如果第一次调用next 的话,cursor等于1,lastRet 等于0 ]
六. Itr 的remove方法
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
直接移除lastRet 位置元素, 也就是前一次next方法返回的元素。之后又把lastRet 置为-1 ,所以不能连续调用两次remove方法,必须要next了,才能remove。
七. for each 到底底层是什么
public static void main(String[] args) throws Exception {
ArrayList list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
for (String str : list) {
System.err.println(str);
}
}
我们利用javap -c 反编译上述代码发现,其实for each语法糖编译后直接就是转换成Iterator 的三步操作:
Iterator it = list.iterator();
while(it.hasNext()) {
System.err.println(it.next());
}
反编译后
七. iterator和iterable的关系
先看下两个接口实现
public interface Iterable {
Iterator iterator();
}
public interface Iterator {
boolean hasNext();
E next();
}
可以发现iterator 才是真的遍历集合的实现,Iterable 只是返回Iterator,代表是可迭代,可for each(遍历)的。
Java容器中,所有的Collection子类会实现Iteratable接口代表有for each功能。
思考:为什么集合不直接实现Iterator方法,而是要通过实现Iteratable 来返回 iterator 对象?
因为集合可能不止一个iterator 实现。例如LinkedList中的ListItr和DescendingIterator两个内部类,就分别实现了双向遍历和逆序遍历。通过返回不同的Iterator实现不同的遍历方式,这样更加灵活。
《 圣人常无心,以百姓心为心 》
释义:圣人常常是没有私心的,以百姓的心为自己的心