Java Collection Framework (Iterator) - HolmesJJ/OOP-FP GitHub Wiki
- All the Java Collection classes can use
for each
loop,List
,Set
andQueue
will iterate each element,Map
will iterate each key. TakeList
as an example:
List<String> list = List.of("Apple", "Orange", "Pear");
for (String s : list) {
System.out.println(s);
}
- In fact, the Java compiler does not know how to traverse the
List
. The reason why the above code can be compiled is because the compiler rewrites thefor each
loop to a normalfor
loop throughIterator
:
for (Iterator<String> iterator = list.iterator(); it.hasNext();) {
String str = iterator.next();
System.out.println(str);
}
Method | Return Type | Description |
---|---|---|
hasNext() |
boolean | If the next element in the collection can be iterated, it returns true |
next() |
E | Return the next element of the iteration. Note: If there is no next element, NoSuchElementException will be thrown |
remove() |
void | Remove the next element |
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
/* Output */
[a, b, c]
a
b
c
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String next = iterator.next();
System.out.println(next);
}
/* Output */
[a, b, c]
a
b
c
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
System.out.println(list);
/* Output */
[a, b, c]
[]
- Each Collection has its "get()" method to get element, and the definition of this method of each Collection is same. However, the data structure of each Collection is different, so the specific way to implement this method is different.
- In order to abstract the "get()" method of getting element, the
Iterable
interface appeared. Each Collection inherits theIterable
interface, and implements the internal class of this interface in different ways (different data structures). This interface has only one methodpublic Iterator<T> iterator()
for the Collection to get its own iterator.
If we write a Collection class and want to use the for each
loop, we only need to implement the following conditions:
- The Collection class implements the
Iterable
interface, which requires to return anIterator
object. - Use
Iterator
object to iterate the internal data of the Collection.
The key point here is that the Collection class returns an Iterator
object by calling the iterator() method. This object itself must know how to traverse this Collection.
class ReverseList<T> implements Iterable<T> {
private List<T> list = new ArrayList<>();
public void add(T t) {
list.add(t);
}
@Override
public Iterator<T> iterator() {
return new ReverseIterator(list.size());
}
class ReverseIterator implements Iterator<T> {
int index;
ReverseIterator(int index) {
this.index = index;
}
@Override
public boolean hasNext() {
return index > 0;
}
@Override
public T next() {
index--;
return ReverseList.this.list.get(index);
}
}
}
public static void iteratorExample() {
ReverseList<String> rlist = new ReverseList<>();
rlist.add("a");
rlist.add("b");
rlist.add("c");
for (String s : rlist) {
System.out.println(s);
}
}
/* Output */
[a, b, c]
a
b
c
- The client can always traverses various Collections in a unified way without caring about their internal data structures.
For example, we know that ArrayList
stores elements in the form of array, and it also provides a get(int)
method. Although we can traverse it with a for
loop:
for (int i = 0; i < list.size(); i++) {
Object value = list.get(i);
}
At this moment, the client must know the internal data structure of the Collection. At the same time, if the ArrayList
is replaced with LinkedList
, the time consumed by the get(int)
method will increase as the index increases. If the ArrayList
is replaced with Set
, the above code will not compile because there is no index inside Set
.
However, there is no such problem using Iterator
, because the Iterator
object is created internally by the Collection object itself, and it knows how to efficiently traverse the data inside the Collection, the client also obtains a unified code so that the compiler can automatically convert the standard for
loop to Iterator
.
- When using
Iterator
, do not change the number of elements of the Collection using the Collection methods such asadd()
andremove()
, otherwise the structure of theIterator
will be destroyed.
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
// Get the element from the Collection
String str = iterator.next();
if ("b".equals(str)) {
// Remove the element using Collection method remove()
list.remove(str);
} else {
System.out.print(str + " ");
}
}
System.out.println("\nAfter removing the element \"b\": " + list);
/* Output */
a
After removing the element "b": [a, c]
After using the Collection method remove()
, the structure of the Iterator
is destroyed and the traversal is stopped.
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
// Get the element from the Collection
String str = iterator.next();
if ("b".equals(str)) {
// Remove the element using Iterator method remove()
iterator.remove();
} else {
System.out.print(str + " ");
}
}
System.out.println("\nAfter removing the element \"b\": " + list);
/* Output */
a c
After removing the element "b": [a, c]
After using the Iterator
method remove()
, the iterator continues to traverse the elements.