Java Iterator 迭代器学习笔记

相信 “迭代” 对于 Java 程序员来说并不陌生(当然,其他语言的程序员也是如此),在处理数据时,不可避免地会存在对数据的大量遍历操作。对于我自己而言,学习使用Java语言两年时间,对于“迭代”的概念还停留在 for:each,for:i的遍历阶段。今天用到了JDK 提供的迭代接口进行 Java 集合的迭代,便决定作以笔记,留作以后学习。

普通 Java 集合迭代(遍历)

迭代可以简单理解为遍历,在没有 JDK 并未提供迭代器时,我们对于数据的遍历处理如下:

  • 对于数组的遍历处理:
1
2
3
4
5
6
Bean[] beans = new Bean[5];

for (int i = 0; i < beans.length; i++) {
beans[i] = new Bean();
// todo something
}
  • 对 ArrayList 的遍历处理:
1
2
3
4
5
ArrayList<Bean> beanArrayList = new ArrayList<Bean>();
for (int i = 0; i < beanArrayList.size(); i++) {
beanArrayList.add(new Bean());
// todo something
}

可以看出,如果我们想对一个空的 Java 集合添加数据,那我们就必须知道该集合内部的数据结构(即,以数组和 ArrayList 为例,添加时的操作应该是beans[i] = new Bean();还是beanArrayList.add(new Bean());),这样就导致了访问逻辑与集合的结构本身紧密耦合(即,每一种不同的数据集合对应了不同的便利方法),这会使得代码无法复用。

Iterator 可以轻松的解决上述问题.

通过java.util.Iterator迭代器

看一下官方未出的解释:

public interface Iterator<E>
An iterator over a collection. Iteratortakes the place of Enumeration
in the Java Collections Framework. Iterators differ from enumerations in two ways:

  • Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
  • Method names have been improved.This interface is a member of the Java Collections Framework

就是说,Iterator是一个对 collection 集合进行迭代的迭代器(接口)。Iterator迭代器取代了 Java Collections Framework 中的 Enumeration。该接口是 Java Collections Framework 的成员。迭代器与枚举有两点不同:

  • 迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。
  • 方法名称得到了改进。

这也就意味着,Iterator 使用相同的访问逻辑完成集合的遍历,使得我们在访问集合元素时,无需关心不同的集合内部结构。从而降低访问逻辑与集合本身的耦合度。

Iterator接口源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package java.util;

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());
}
}

如何使用 Iterator 对数据遍历

在使用中,我们既可以借助hasNext()next()方法,对已知集合结构的集合对象完成简单的迭代(类似于之前的方案),就像这样:

1
2
3
4
5
Iterator<Bean> beanIterator = beanArrayList.iterator();
while (beanIterator.hasNext()) {
Bean bean = beanIterator.next();
// todo something
}

更新于2017.12.03

更符合封装思想的迭代手段

仅仅这样不是与之前的方案没什么区别了吗?当然,它能做的还可以更多。做到真正不去关心集合内部结构的迭代手段,下述代码是对一个集合对象的迭代:

1
2
3
4
5
6
7
8
9
10
11
12
public static boolean isExist(Collection<Bean> beans, Bean bean) {
Iterator<Bean> iterator = beans.iterator();
System.out.println(beans.getClass().getSimpleName() + ":");
while (iterator.hasNext()) {
if (iterator.next().equals(bean)) {
System.out.println("集合中已存在 Bean[" + bean + "]");
return true;
}
}

return false;
}

Test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static final void main(String[] args) {
Bean bean0 = new Bean().setId("01");
Bean bean1 = new Bean().setId("02");

ArrayList<Bean> beanArrayList = new ArrayList<>();
Queue<Bean> beanQueue = new LinkedBlockingQueue<>();
HashMap<Bean, Bean> map = new HashMap<>();

beanArrayList.add(bean0);
beanArrayList.add(bean1);

map.put(bean0, bean0);
map.put(bean1, bean1);

beanQueue.add(bean0);
beanQueue.add(bean1);

isExist(map.keySet(), bean0);
isExist(beanArrayList, bean1);
isExist(beanQueue, bean1);
}

输出结果:

1
2
3
4
5
6
KeySet:
集合中已存在 Bean[Bean{id='01', password='null'}]
ArrayList:
集合中已存在 Bean[Bean{id='02', password='null'}]
LinkedBlockingQueue:
集合中已存在 Bean[Bean{id='02', password='null'}]

总结

所有Collection框架中的j几乎所有的集合类(或接口)(如ArrayList、Set、Queue等)都实现了(接口也声明了iterator()方法)自己的内部的迭代器,但无疑都是Iterator的实现。

不过据数组由于其本身的实现方案与 “编译处理和 JVM 的native()方法有关,不能直接获取到其本身的迭代器,但可通过集合框架将数组转换为集合类型就好了。