迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象中的各个元素,而不用暴露这个对象的内部表示。在Java中,ArrayList的迭代器有两种:Iterator和ListIterator。
Iterator
迭代器是一个用来遍历并选择序列中的对象。Java的Iterator的只能 单向移动 。
例子
在写如何实现之前,先看一个使用迭代器Iterator的小例子:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
@org.junit.Test
public void test() {
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String str = (String) iterator.next();
System.out.println(str);
iterator.remove();
}
System.out.println(list.size());
}
}
运行结果
1
2
3
4
0
方法
- iterator(),list.iterator()用来从容器对象中返回一个指向list开始处的迭代器。
- next(),获取序列中的下个元素。
- hasNext(),检查序列中向下是否还有元素。
- remove(),将迭代器最近返回的一个元素删除。这也意味着调用remove()前需要先调用next()。
实现
在设计模式(16)-迭代器模式这一文章中曾讲过,迭代器模式中有四个角色:
- 抽象聚合类Aggregate。在ArrayList的Iterator迭代器实现中,没有抽象聚合类。虽然它实现了AbstractList,实现了List,但它向外部提供的创建迭代器的方法iterator()是它自己的。
- 具体聚合类ConcreteAggregate。在ArrayList的Iterator迭代器实现中,指的是ArrayList。
- 抽象迭代器Iterator。在ArrayList的Iterator迭代器实现中,指的是Iterator接口。
- 具体迭代器ConcreteIterator。在ArrayList中的Iterator迭代器实现中,指的是Itr。
ArrayList代码片段
public class ArrayList<E>
{
/**
* 保存添加到ArrayList中的元素。
* ArrayList的容量就是该数组的长度。
* 该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入ArrayList中时,数组将扩容值DEFAULT_CAPACITY。
* 被标记为transient,在对象被序列化的时候不会被序列化。
*/
transient Object[] elementData;
// ArrayList的实际大小(数组包含的元素个数)。
private int size;
/**
* 返回一个用来遍历ArrayList元素的Iterator迭代器
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* AbstractList.Itr的最优化的版本
*/
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回的元素的索引
int lastRet = -1; // 最近的被返回的元素的索引; 如果没有返回-1。
int expectedModCount = modCount;
/**
* 判断是否有下一个元素
*/
public boolean hasNext() {
//如果下一个要返回的元素的索引不等于ArrayList的实际大小,则返回false
return cursor != size;
}
/**
* 返回下一个元素
*/
@SuppressWarnings("unchecked")
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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
Iterator
import java.util.function.Consumer;
public interface Iterator<E> {
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());
}
}
Itr.java
作为ArrayList的内部类。请在上文中的[ArrayList.java代码片段]中查看。
ListIterator
ListIterator是一个更加强大的Iterator的子类型。它只能用于各种List类的访问。它最大的优点是可以 双向移动 。它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。
例子
在写如何实现之前,先看一个使用列表迭代器ListIterator的小例子:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Test {
@org.junit.Test
public void test() {
List list = new ArrayList<>();
list.add("0");
list.add("1");
list.add("2");
list.add("3");
ListIterator iterator = list.listIterator();
System.out.println("--------------------向下遍历--------------------");
while (iterator.hasNext()) {
int nextIndex = iterator.nextIndex();
String next = (String) iterator.next();
//int previousIndex = iterator.previousIndex();
System.out.println("当前元素:"+next+",当前元素索引:"+nextIndex/*+",前一个元素的索引"+previousIndex*/);
}
System.out.println("--------------------向上遍历--------------------");
while (iterator.hasPrevious()) {
int previousIndex = iterator.previousIndex();
String previous = (String) iterator.previous();
System.out.println("当前元素:"+previous+",当前元素索引:"+previousIndex);
}
System.out.println("-----------测试set()和listIterator(n)----------");
System.out.println(list);
iterator = list.listIterator(3);
while(iterator.hasNext()){
iterator.next();
iterator.set("5");
}
System.out.println(list);
}
}
运行结果
-------向下遍历-------
当前元素:0,当前元素索引:0
当前元素:1,当前元素索引:1
当前元素:2,当前元素索引:2
当前元素:3,当前元素索引:3
-------向上遍历-------
当前元素:3,当前元素索引:3
当前元素:2,当前元素索引:2
当前元素:1,当前元素索引:1
当前元素:0,当前元素索引:0
-------测试set()和listIterator(n)-------
[0, 1, 2, 3]
[0, 1, 2, 5]
方法
- listIterator(),list.iterator()用来从容器对象中返回一个指向list开始处的迭代器。
- listIterator(n),list.iterator()用来从容器对象中返回一个指向列表索引为n的迭代器。
- next(),获取序列中的下个元素,运行后索引+1。
- previous(),获取序列中的上个元素,运行后索引-1。
- nextIndex,获取序列中的下个元素的索引。
- previousIndex,获取序列中的下个元素的索引。
- hasNext(),检查序列中向下是否还有元素。
- hasPrevious(),检查序列中向上是否还有元素。
- remove(),从列表中删除next()或previous()返回的最后一个元素。这也意味着调用remove()前需要先调用next()或者previous()。
- add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前。
- set(E e):从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e。
实现
在设计模式(16)-迭代器模式这一文章中曾讲过,迭代器模式中有四个角色:
- 抽象聚合类Aggregate。在ArrayList的ListIterator迭代器实现中,没有抽象聚合类。虽然它实现了AbstractList,实现了List,但它向外部提供的创建迭代器的方法listIterator()是它自己的。
- 具体聚合类ConcreteAggregate。在ArrayList的ListIterator迭代器实现中,指的是ArrayList。
- 抽象迭代器Iterator。在ArrayList的ListIterator迭代器实现中,指的是ListIterator。
- 具体迭代器ConcreteIterator。在ArrayList中的ListIterator迭代器实现中,指的是ListItr。
ArrayList代码片段
public class ArrayList<E>
{
/**
* 保存添加到ArrayList中的元素。
* ArrayList的容量就是该数组的长度。
* 该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入ArrayList中时,数组将扩容值DEFAULT_CAPACITY。
* 被标记为transient,在对象被序列化的时候不会被序列化。
*/
transient Object[] elementData;
// ArrayList的实际大小(数组包含的元素个数)。
private int size;
/**
* 返回一个一开始就指向列表索引为index的元素处的ListIterator
*
* @throws IndexOutOfBoundsException
*/
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
/**
* 返回一个一开始就指向列表索引为0的元素处的ListIterator
*
* @see #listIterator(int)
*/
public ListIterator<E> listIterator() {
return new ListItr(0);
}
/**
* AbstractList.ListItr的最优化版本
*/
private class ListItr extends Itr implements ListIterator<E> {
//用来从list中返回一个指向list索引为index的元素处的迭代器
ListItr(int index) {
super();
cursor = index;
}
//获取list中的上个元素
public boolean hasPrevious() {
return cursor != 0;
}
//获取list中的下个元素的索引
public int nextIndex() {
return cursor;
}
//获取list中的上个元素的索引
public int previousIndex() {
return cursor - 1;
}
//获取list中的上个元素
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
//从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
//将指定的元素插入列表,插入位置为迭代器当前位置之前。
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
Iterator
import java.util.function.Consumer;
public interface Iterator<E> {
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());
}
}
ListItr.java
作为ArrayList的内部类。请在上文中的[ArrayList.java代码片段]中查看。
总结
什么是fail-fast?
细心地朋友看文章或源码时一定会发现在iterator()和listIterator()的注释中都有一句话: 返回的迭代器是fail-fast的 。什么是fail-fast?
详情请移步fail-fast详解
好了,关于迭代器模式与ArrayList就写到这里。有什么需要补充的欢迎在下方留言^^。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。