Java Collection Framework (Iterator) - nus-cs2030/2021-s1 GitHub Wiki
- All the Java Collection classes can use
for eachloop,List,SetandQueuewill iterate each element,Mapwill iterate each key. TakeListas 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 eachloop to a normalforloop 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
Iterableinterface appeared. Each Collection inherits theIterableinterface, 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
Iterableinterface, which requires to return anIteratorobject. - Use
Iteratorobject 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 theIteratorwill 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.