1. Background on how iterators came to be
You are already familiar with
HashSet. If you've really investigated it, beyond just reading a lesson, then you should have asked this question:
How do I display a list of all HashSet elements on the screen? After all, the interface does not have
HashSet is not alone in this limitation. In addition to
HashSet, there are many other collections that do not allow elements to be retrieved by index, because the elements have no defined order.
Over the years, programmers have invented lots of complex data structures, such as graphs and trees. Or lists of lists.
Many containers change the order of their elements when new elements are added or existing elements are removed. For example, a list stores elements in a particular order, and when a new element is added, it is almost always inserted in the middle of the list.
And we also get situations where there is a container that stores elements but not in any fixed order.
Now let's say we want to copy all the elements from such a collection into an array or list. We need to get all the elements. We don't care about the order in which we iterate over the elements — the important thing is not to iterate over the same elements more than once. How do we do that?
2. Iterator for a collection
Iterators were proposed as a solution to the problem above.
An iterator is a special object associated with a collection, which helps traverse all the elements of the collection without repeating any.
You can use the following code to get an iterator for any collection:
Iterator<Type> it = name.iterator();
name is the name of collection variable,
Type is the type of the elements of the collection,
iterator() is one of the collection's methods, and
it is the name of the iterator variable.
An iterator object has 3 methods:
|Returns the next element in the collection|
|Checks whether there are any elements that have not been traversed yet|
|Removes the current element of the collection|
These methods are somewhat similar to the Scanner class's
next() method returns the next element of the collection from which we got the iterator.
hasNext() method checks whether the collection has additional elements that the iterator has not returned yet.
Here's how to display all the elements of a
We add greetings in various languages to the
Get an iterator object for the
As long as there are still elements
Get the next element
Display the element on the screen
The main disadvantage of an iterator is that your code becomes more cumbersome than using a
To compare, let's display a list using a
for loop and also using an iterator:
Yes, it's much better to traverse the elements of an
ArrayList using a loop — everything turns out to be shorter.
But Java's creators again decided to pour some sugar on us. Luckily for us, it was syntactic sugar.
They gave Java a new kind of loop and called it a
for-each loop. This is how it looks in general:
collection is the name of the collection variable,
Type is the type of the elements in the collection, and
name is the name of a variable that takes the next value from the collection at each iteration of the loop.
This kind of loop iterates through all the elements of a collection using an implicit iterator. This is how it actually works:
|For-each loop||What the compiler sees: Loop with an iterator|
When the compiler encounters a
for-each loop in your code, it simply replaces it with the code on the right: it adds a call to get an iterator along with any other missing method calls.
Programmers love the
for-each loop and almost always use it when they need to iterate over all of the elements of a collection.
Even iterating over an
ArrayList list using a
for-each loop looks shorter:
|For-each loop||for loop|
4. Removing an element in a
for-each loop has one drawback: it can't remove elements correctly. If you write code like this, you'll get an error.
The remove operation will generate an error!
This is a very nice and understandable code, but it won't work.
You can't change a collection while you are traversing it with an iterator.
There are three ways to get around this limitation.
1. Use a different kind of loop
When traversing an ArrayList collection, you can use an ordinary loop with an
i counter variable.
However, this option is not suitable for
2. Use an explicit iterator
You can use an iterator explicitly and call its
|Version that works||Version that doesn't work|
Note that we call the
remove() method on the iterator object! The iterator is aware that the item has been removed and can handle the situation correctly.
3. Use a copy of the collection
You can also create a copy of the collection and then use the copy in a
for-each loop and delete elements from the original collection.
|Creating a copy of a collection is super easy
The loop uses the iterator for the copy of the collection.
Elements are removed from the
The collection is copied rather quickly, since the elements themselves are not duplicated. Instead, the new collection stores references to the elements that already exist in the old collection.