集合元素的迭代
使用 for-each 模式
迭代集合元素最简单的选择是使用 for-each 模式。
Collection<String> strings = List.of("one", "two", "three");
for (String element: strings) {
System.out.println(element);
}
运行此代码会产生以下结果
one
two
three
只要您只需要读取集合的元素,这种模式就非常有效。 Iterator
模式允许在迭代集合时删除一些元素。如果您需要这样做,那么您需要使用 Iterator
模式。
在集合上使用迭代器
迭代集合的元素使用一个特殊对象,即 Iterator
接口的实例。您可以从 Collection
接口的任何扩展中获取 Iterator
对象。 iterator()
方法在 Iterable
接口上定义,由 Collection
接口扩展,并由集合层次结构中的所有接口进一步扩展。
使用此对象迭代集合元素是一个两步过程。
如果您调用 next()
方法,但集合中没有更多元素,您将获得一个 NoSuchElementException
。调用 hasNext()
不是强制性的,它可以帮助您确保确实存在下一个元素。
以下是模式
Collection<String> strings = List.of("one", "two", "three", "four");
for (Iterator<String> iterator = strings.iterator(); iterator.hasNext();) {
String element = iterator.next();
if (element.length() == 3) {
System.out.println(element);
}
}
此代码会产生以下结果
one
two
Iterator
接口有第三个方法: remove()
。调用此方法会从集合中删除当前元素。但是,在某些情况下,此方法不受支持,它将抛出一个 UnsupportedOperationException
。很明显,在不可变集合上调用 remove()
无法工作,因此这是其中一种情况。您从 ArrayList
、 LinkedList
和 HashSet
获得的 Iterator
的实现都支持此删除操作。
在迭代集合时更新集合
如果您碰巧在迭代集合时修改了集合的内容,您可能会得到一个 ConcurrentModificationException
。获得此异常可能有点令人困惑,因为此异常也用于并发编程。在集合框架的上下文中,您可能会在不接触多线程编程的情况下获得它。
以下代码会抛出一个 ConcurrentModificationException
。
Collection<String> strings = new ArrayList<>();
strings.add("one");
strings.add("two");
strings.add("three");
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
strings.remove(element);
}
如果您需要删除满足给定条件的集合元素,您可以使用 removeIf()
方法。
实现 Iterable 接口
现在您已经了解了集合框架中的迭代器,您可以创建 Iterable
接口的简单实现。
假设您需要创建一个 Range
类来模拟两个限制之间的整数范围。您需要做的就是从第一个整数迭代到最后一个整数。
您可以使用记录来实现 Iterable
接口,这是 Java SE 16 中引入的功能
record Range(int start, int end) implements Iterable<Integer> {
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
private int index = start;
@Override
public boolean hasNext() {
return index < end;
}
@Override
public Integer next() {
if (index > end) {
throw new NoSuchElementException("" + index);
}
int currentIndex = index;
index++;
return currentIndex;
}
};
}
}
如果您应用不支持 Java SE 16,则可以使用普通类执行相同的操作。请注意, Iterator
实现的代码完全相同。
class Range implements Iterable<Integer> {
private final int start;
private final int end;
public Range(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
private int index = start;
@Override
public boolean hasNext() {
return index < end;
}
@Override
public Integer next() {
if (index > end) {
throw new NoSuchElementException("" + index);
}
int currentIndex = index;
index++;
return currentIndex;
}
};
}
}
在这两种情况下,您都可以在 for-each 语句中使用 Range
的实例,因为它实现了 Iterable
for (int i : new Range1(0, 5)) {
System.out.println("i = " + i);
}
运行此代码会产生以下结果
i = 0
i = 1
i = 2
i = 3
i = 4
上次更新: 2021 年 9 月 14 日