An iterator is a design pattern that separates the traversal behavior of a sequence-type data structure from the object being traversed, ie we don't need to care what the underlying structure of the sequence looks like. As long as you get this object, you can iterate over the inside of this object using an iterator. When you need to access an aggregate object and need to traverse regardless of what these objects are, you should consider using the iterator pattern.
1, Java iterator interface
Let's first look at the definition of the Collection interface:
public interface Collection<E> extends Iterable<E>
First, it uses a type parameter. Second, it implements the Iterable interface. Let's take a quick look at the definition of the Iterable<E> interface:
public interface Iterable<T> {
Iterator<T> iterator();
}
We can see that this interface only defines a method that requires us to return an object that implements the Iterator<T> interface, so let's look at the definition of the Iterator:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
We have mentioned two interfaces related to iterators: the Iterable interface and the Iterator interface. In the literal sense, the former means "iterable" and the latter means "iterator". So we can understand these two interfaces: the class that implements the Iterable interface is iterable; the class that implements the Iterator interface is an iterator.
An iterator is something we use to iterate over objects in a collection. That is to say, for collections, we don't iterate through direct access to elements like the original type array, but traverse the object through iterators. The advantage of doing this is to separate the traversal behavior of the collection type from the traversed collection object, so that we don't need to care about the concrete implementation of the collection type. As long as you get the iterator of this collection object, you can iterate over the objects in this collection. The details of the order of traversing objects are all handled by its iterator. Now let's sort through the things mentioned above: First, the Collection interface implements the Iterable<E> interface, which means that all concrete collection classes that implement the Collection interface are iterable.
Since we want to iterate, we need an iterator to traverse the objects in the corresponding collection, so the Iterable<E> interface requires us to implement the iterator method, which returns an iterator object. An iterator object is an object that implements the Iterator<E> interface. This interface requires us to implement the three methods methods: hasNext(), next(), and remove(). The hasNext method determines if there is another element (that is, whether the object has been traversed), the next method will return the next element (if there is no next element, calling it will throw a NoSuchElementException), the remove method is used to move Except for the element that was last called by the next method (if the next method is called, the remove method will report an error). We can imagine that before starting to iterate over the collection, there is a pointer to the front of the first element of the collection. After the first call to the next method, the pointer will "sweep" the first element and return it. Calling the hasNext method is Look at this pointer and there are no elements. This means that the pointer always points between the element that has just been traversed and the next element to be traversed. Usually, the code that iterates over a collection object looks like this:
Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while (iter.hasNext()) {
String element = iter.next();
do something with element;
}
Java SE 5.0 provides us with an equivalent, but a more concise version of the above code snippet:
for (String element : c) {
do something with element;
}
It can be seen that the advantage of using the for each loop statement is that it is more concise, less prone to error, and does not care about the start and end values of the subscript. For each is not a keyword, the keyword is still, and the statement is implemented by an iterator.
2, iterator traversal trap
The problem that requires special attention when using iterators to traverse collections is the use of the remove() method. The following code throws an exception while running:
List list = ...;
for(Iterator iter = list.iterator(); iter.hasNext();) {
Object obj = iter.next();
...
if(***) {
list.remove(obj);
}
}
After executing the remove method, and then executing the loop iter.next(), report Java.util.ConcurrentModificationException (if remove is the last element, it will not execute the next() operation), in order to explore the problem. The root cause, look at the source code of the Iterator interface and Collection interface:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
public interface Collection<E> extends Iterable<E> {
...
Iterator<E> iterator();
boolean add(E o);
boolean remove(Object o);
...
}
There are two remove methods here. Generally, when deleting and adding elements, it is customary to call the specific collection's own method, which is the remove method defined in the Collection interface. E.g:
List list = new ArrayList();
list.add(...);
list.remove(...);
However, if you call the collection's own remove() method during the iterator loop traversal, it will cause a loop error, because the size of list.size() changes during the loop, causing an error. So, if you want to remove an element from the collection in a loop statement, use the remove() method provided by the iterator iterator because its remove() method not only removes the element, it also maintains a flag for recording. Is it currently possible to delete the state, for example, you can't call its remove() method twice in succession, at least once before the call to the next() method. Similarly, the collection's own add() method cannot be called during the iterator loop traversal.
3, iterator principle and fast failure
When you use the iterator to traverse the container, you actually create a data structure on the iterator. The iterator uses this data structure to access the container. If you use the container's own add and remove methods to increase or decrease, this time The data structure produced by the iterator does not change, so the iterator loop will throw an exception. If you use the iterator iterator itself to increase or decrease, the corresponding data structure will change.
Iterator works in a separate thread and has a mutex lock. After the Iterator is created, it will create a single-chain index table pointing to the original collection. When the number of elements in the original collection changes, the contents of the index table will not change synchronously. When the index pointer moves backwards, it will appear. To the case of iterating over the object. According to the "fast failure" principle (the word "fast failure" often appears in the official Java API documentation), Iterator will throw a java.util.ConcurrentModificationException at this time. So the Iterator does not allow changes to the iterated collection when it's working, but you can remove or add objects using the methods provided by Iterator itself remove() or add(). The Iterator.remove() method will delete the current iteration. The index table is updated at the same time as the object.
"Fast failure" means that when a thread is iterating over a Collection, it usually does not allow other threads to modify the contents of the Collection, because the result of the iterator iteration will be inaccurate. For example, when iterator is used to iterate the collection, the iterator is another. A thread that is traversing the collection by this additional thread. If the collection.remove(obj) method is used to modify the contents of the collection, a ConcurrentModificationException will occur, and the iterator will fail quickly.