1.Iterator
使用JDK提供的迭代接口进行Java集合的迭代:
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String string = iterator.next();
//do something
}
迭代器是一个标准化遍历各类容器里面的所有对象的方法类,它采用很典型的设计模式——迭代器模式。
通过迭代器模式可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。即遍历容器中的对象不依赖于容器内部结构,所有的内部状态都由Iterator来维护,然后采用同一种逻辑来遍历集合
在Java中,Iterator是一个接口,其主要定义了以下三个方法:
public interface Iterator {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
ArrayList中Iterator的实现
通过iterator()返回迭代器
public Iterator<E> iterator() {
return new Itr();
}
Itr的实现如下
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引位置
int lastRet = -1; // 上一个元素的索引位置
int expectedModCount = modCount;
Itr() {}
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的remove()方法删除元素
ArrayList.this.remove(lastRet);
// 删除后由于数组前移,当前位置为遍历的下一个元素
cursor = lastRet;
// 上一个元素先重置为-1,下次遍历时再重新记录
lastRet = -1;
// 修改expectedModCount为当前的modCount,因此用迭代器进行遍历删除不会报ConcurrentModificationException()异常
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
2.Iterable
Iterable是Java中的一个接口,表示可以进行迭代,其主要实现如下:
public interface Iterable<T> {
Iterator<T> iterator();
}
可以看到,Iterable接口规定了其实现类需要重写iterator()方法,并通过该方法返回该容器的迭代器
而Collection接口继承自Iterable
public interface Collection<E> extends Iterable<E>
因此,所有实现了Collection接口的容器类都需要自己实现自己的迭代器逻辑。在外部可以使用该迭代器进行统一迭代
实现了 Iterable后,即可使用下面两种迭代方法遍历容器
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + ",");
}
// 增强for的本质就是使用iterator,在编译时翻译成使用iterator的逻辑
for (Integer i : list) {
System.out.print(i + ",");
}
普通for和增强for遍历的比较
下面是两种迭代方式:
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}
for (Integer i : list) {
System.out.print(i + ",");
}
两者比较:
- 普通for是根据下标索引来从容器中获取元素,增强for是通过迭代器来获取元素
- 对于基于数组实现的容器,由于数组支持快速随机访问,能根据下标快速访问到元素,因此两者之间的性能差距不大。普通for要比增强for稍快一点
- 而基于链表实现的容器,通过下标获取元素每次都要从头开始访问,性能极差。而迭代器由于记录了下一个元素的位置,因此很快就能获取下一个元素。此时增强for要比普通for快很多
RandomAccess
RandomAccess用来当标记,是一种标记接口,代表其实现类支持快速随机访问。
用处是当要实现某些算法时,会判断当前类是否实现了RandomAccess接口,会选择不同的算法。
接口RandomAccess中内容是空的,只是作为标记用。比如List下的ArrayList和LinkedList。其中ArrayList实现了RandomAccess。而LinkedList没有。我们可以利用instanceof来判断哪一个是实现了RandomAccess。分辨出两个集合。其中ArrayList使用for循环遍历快,而LinkedList使用迭代器快。那么通过分辨,不同的集合使用不同的遍历方式。
所以说RandomAccess只是一个空的,用来标记的接口。