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 get()
and set()
methods!
And 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();
Where 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:
Method | Description |
---|---|
|
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 nextInt)
and hasNextInt()
methods.
The next()
method returns the next element of the collection from which we got the iterator.
The 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 HashSet
:
Code | Notes |
---|---|
|
Create a HashSet object that stores String elements.We add greetings in various languages to the set variable.Get an iterator object for the set set.As long as there are still elements Get the next element Display the element on the screen |
3. For-each
loop
The main disadvantage of an iterator is that your code becomes more cumbersome than using a for
loop.
To compare, let's display a list using a for
loop and also using an iterator:
Iterator | for loop |
---|---|
|
|
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:
for(Type name:collection)
Where 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
The for-each
loop has one drawback: it can't remove elements correctly. If you write code like this, you'll get an error.
Code | Note |
---|---|
|
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.
Code |
---|
|
However, this option is not suitable for HashSet
and HashMap
collections
2. Use an explicit iterator
You can use an iterator explicitly and call its remove()
method.
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.
Code | Note |
---|---|
|
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 list collection.
|
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.
GO TO FULL VERSION