1. Overview
1.概述
In this tutorial, we’ll look at the usage of Iterable and Iterator interfaces in Java and the differences between them.
在本教程中,我们将了解Java中Iterable和Iterator接口的用法以及它们之间的区别。
2. Iterable Interface
2.Iterable接口
Iterable interface belongs to the java.lang package. It represents a data structure that can be iterated over.
Iterable接口属于java.lang包。它表示一个可以被迭代的数据结构。
The Iterable interface provides a method that produces an Iterator. When using an Iterable, we cannot get an element by index. Similarly, we cannot get the first or the last elements from the data structure as well.
Iterable接口提供了一个方法来产生一个Iterator。当使用Iterable时,我们不能通过索引获得一个元素。同样地,我们也不能从数据结构中获得第一个或最后一个元素。
All collections in Java implement the Iterable interface.
Java中的所有集合都实现了Iterable接口。
2.1. Iterate Over an Iterable
2.1.遍历一个Iterable
We can iterate over elements inside a collection using the enhanced for loop, also called the for-each loop. However, only objects that implement an Iterable interface can be used within such a statement. It’s also possible to iterate over elements using the while statement in combination with an Iterator.
我们可以使用增强的for循环来迭代集合中的元素,也称为for-each>循环。然而,只有实现Iterable接口的对象才能在这样的语句中使用。也可以使用while语句与Iterator相结合来迭代元素。
Let’s see an example of iterating over elements in a List using the for-each statement:
让我们看一个使用for-each语句在List中迭代元素的例子。
List<Integer> numbers = getNumbers();
for (Integer number : numbers) {
System.out.println(number);
}
Similarly, we can use the forEach() method in combination with lambda expressions:
类似地,我们可以将forEach()方法与lambda表达式结合使用。
List<Integer> numbers = getNumbers();
numbers.forEach(System.out::println);
2.2. Implementing the Iterable Interface
2.2.实现Iterable接口
Custom implementations of the Iterable interface can come in handy when we have custom data structures that we’d like to iterate over.
当我们有想要迭代的自定义数据结构时,Iterable接口的自定义实现可以派上用场。
Let’s begin by creating a class representing a shopping cart that will hold elements in an array. We’ll not call the for-each loop directly on the array. Instead, we’ll implement the Iterable interface. We don’t want our clients to depend on the chosen data structure. If we provide clients the ability to iterate, we can easily use a different data structure without clients having to change code.
让我们首先创建一个代表购物车的类,它将在一个数组中保存元素。我们将不直接在数组上调用for-each循环。相反,我们将实现Iterable接口。我们不希望我们的客户依赖于所选择的数据结构。如果我们为客户提供了迭代的能力,我们就可以轻松地使用不同的数据结构,而无需客户改变代码。
The ShoppingCart class implements the Iterable interface and overrides its iterate() method:
ShoppingCart类实现了Iterable接口并重写了其iterate()方法。
public class ShoppingCart<E> implements Iterable<E> {
private E[] elementData;
private int size;
public void add(E element) {
ensureCapacity(size + 1);
elementData[size++] = element;
}
@Override
public Iterator<E> iterator() {
return new ShoppingCartIterator();
}
}
The add() method stores elements in an array. Due to an array’s fixed size and capacity, we expand the maximum number of elements using the ensureCapacity() method.
add()方法在一个数组中存储元素。由于数组的固定大小和容量,我们使用ensureCapacity()方法来扩展元素的最大数量。
Each invocation of the iterator() method on the custom data structure produces a new instance of an Iterator. We create a new instance since an iterator is responsible for maintaining the current iteration state.
对自定义数据结构的iterator()方法的每次调用都会产生一个新的Iterator实例。我们创建一个新的实例,因为迭代器负责维护当前的迭代状态。
By providing a concrete implementation of the iterator() method, we can use an enhanced for statement to iterate over objects of the implemented class.
通过提供iterator()方法的具体实现,我们可以使用增强的for语句来迭代实现的类的对象。
Now, let’s create an inner class inside the ShoppingCart class that represents our custom iterator:
现在,让我们在ShoppingCart类中创建一个内类,代表我们的自定义迭代器。
public class ShoppingCartIterator implements Iterator<E> {
int cursor;
int lastReturned = -1;
public boolean hasNext() {
return cursor != size;
}
public E next() {
return getNextElement();
}
private E getNextElement() {
int current = cursor;
exist(current);
E[] elements = ShoppingCart.this.elementData;
validate(elements, current);
cursor = current + 1;
lastReturned = current;
return elements[lastReturned];
}
}
Lastly, let’s create an instance of our iterable class and use it in the enhanced for loop:
最后,让我们创建一个iterable类的实例,并在增强的for循环中使用它。
ShoppingCart<Product> shoppingCart = new ShoppingCart<>();
shoppingCart.add(new Product("Tuna", 42));
shoppingCart.add(new Product("Eggplant", 65));
shoppingCart.add(new Product("Salad", 45));
shoppingCart.add(new Product("Banana", 29));
for (Product product : shoppingCart) {
System.out.println(product.getName());
}
3. Iterator Interface
3.迭代器接口
Iterator is a member of the Java Collections Framework. It belongs to the java.util package. This interface allows us to retrieve or remove elements from a collection during the iteration.
迭代器是Java集合框架的一个成员。它属于java.util包。这个接口允许我们在迭代过程中从集合中检索或移除元素。
In addition, it has two methods that help iterate over the data structure and retrieve its elements – next() and hasNext().
此外,它有两个方法帮助迭代数据结构并检索其元素 – next() 和 hasNext()。
Moreover, it has a remove() method, which removes the current element pointed to by the Iterator.
此外,它有一个remove()方法,可以删除Iterator所指向的当前元素。
Finally, the forEachRemaining(Consumer<? super E> action) method performs the given action for each remaining element inside the data structure.
最后,forEachRemaining(Consumer<? super E> action)方法对数据结构内的每个剩余元素执行给定的操作。
3.1. Iterate Over Collection
3.1.在集合上迭代
Let’s see how to iterate over a List of Integer elements. In the example, we’ll combine the while loop and methods hasNext() and next().
让我们看看如何在一个List的Integer元素上迭代。在这个例子中,我们将结合while循环和方法hasNext()和next()。
The List interface is a part of Collection and, therefore, it extends the Iterable interface. To get an iterator from the collection, we simply need to call the iterator() method:
List接口是Collection的一部分,因此,它扩展了Iterable接口。为了从集合中获得一个迭代器,我们只需要调用iterator()方法。
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
Iterator<Integer> iterator = numbers.iterator();
Furthermore, we can check if the iterator has any remaining elements by calling the hasNext() method. Afterward, we can get an element by calling the next() method:
此外,我们可以通过调用hasNext()方法来检查迭代器是否还有其他元素。之后,我们可以通过调用next()方法获得一个元素。
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
The next() method returns the next element from the iteration. On the other hand, if there is no such element, it throws NoSuchElementException.
next()方法返回迭代的下一个元素。另一方面,如果没有这样的元素,它会抛出NoSuchElementException。
3.2. Implementing the Iterator Interface
3.2.实现Iterator接口
Now, we’ll implement the Iterator interface. Custom implementation can be useful when we need to iterate over a collection using conditional element retrieval. For instance, we can use a custom iterator for iterating over odd or even numbers.
现在,我们将实现Iterator接口。当我们需要使用有条件的元素检索来迭代一个集合时,自定义的实现会很有用。例如,我们可以使用一个自定义迭代器来迭代奇数或偶数。
To illustrate, we’re going to iterate over prime numbers from the given collection. As we know, a number is considered a prime if it’s only divisible by one and by itself.
为了说明这一点,我们将在质数上进行迭代。我们知道,如果一个数字只能被1和它本身所整除,那么它就被认为是素数。
First, let’s create a class that contains a collection of numeric elements:
首先,让我们创建一个包含数字元素集合的类。
class Numbers {
private static final List<Integer> NUMBER_LIST =
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
Furthermore, let’s define a concrete implementation of the Iterator interface:
此外,让我们定义一个Iterator接口的具体实现。
private static class PrimeIterator implements Iterator<Integer> {
private int cursor;
@Override
public Integer next() {
exist(cursor);
return NUMBER_LIST.get(cursor++);
}
@Override
public boolean hasNext() {
if (cursor > NUMBER_LIST.size()) {
return false;
}
for (int i = cursor; i < NUMBER_LIST.size(); i++) {
if (isPrime(NUMBER_LIST.get(i))) {
cursor = i;
return true;
}
}
return false;
}
}
Concrete implementations are usually created as inner classes. In addition, they’re responsible for maintaining the current iteration state. In the example above, we stored the current position of the next prime number inside the instance variable. Every time we call the next() method, the variable will contain an index of the upcoming prime number.
具体的实现通常是作为内部类创建的。此外,它们还负责维护当前的迭代状态。在上面的例子中,我们把下一个质数的当前位置存储在实例变量里面。每次我们调用next()方法时,该变量将包含一个即将到来的素数的索引。
Any implementation of the next() method should throw a NoSuchElementException exception when there are no more elements left. Otherwise, the iteration can cause unexpected behavior
任何next()方法的实现都应该在没有更多的元素时抛出一个NoSuchElementException异常。否则,迭代会导致意外的行为
Let’s define a method inside the Number class that returns a new instance of the PrimeIterator class:
让我们在Number类中定义一个方法,返回一个PrimeIterator类的新实例。
public static Iterator<Integer> iterator() {
return new PrimeIterator();
}
Finally, we can use our custom iterator within the while statement:
最后,我们可以在while语句中使用我们的自定义迭代器。
Iterator<Integer> iterator = Numbers.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
4. Differences Between Iterable and Iterator
4.Iterable和Iterator之间的区别
To sum up, the following table shows the main differences between the Iterable and the Iterator interfaces:
总而言之,下表显示了Iterable和Iterator接口之间的主要区别。
Iterable | Iterator |
---|---|
Represents a collection that can be iterated over using a for-each loop | Represents an interface that can be used to iterate over a collection |
When implementing an Iterable, we need to override the iterator() method | When implementing an Iterator, we need to override the hasNext() and next() methods |
Doesn’t store the iteration state | Stores the iteration state |
Removing elements during the iteration isn’t allowed | Removing elements during the iteration is allowed |
5. Conclusion
5.总结
In this article, we looked at the differences between the Iterable and the Iterator interfaces in Java and their usage.
在这篇文章中,我们研究了Java中Iterable和Iterator接口的区别及其用法。
As always, the source code for the examples is available over on GitHub.
一如既往,这些例子的源代码可在在GitHub上获得。