Java Collection Framework (Iterator) - HolmesJJ/OOP-FP GitHub Wiki

Iterator

  • All the Java Collection classes can use for each loop, List, Set and Queue will iterate each element, Map will iterate each key. Take List 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 the for each loop to a normal for loop through Iterator:
for (Iterator<String> iterator = list.iterator(); it.hasNext();) {
     String str = iterator.next();
     System.out.println(str);
}

Methods defined by Iterator

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

Usage of Iterator

Case 1: while loop

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

Case 2: for loop

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

Case 3: Clear all elements in the Collection

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]
[]

Case 4: Iterable

  • 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 the Iterable interface, and implements the internal class of this interface in different ways (different data structures). This interface has only one method public 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 an Iterator 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.

Example

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

Advantage of Iterator

  • 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.

Note

  • When using Iterator, do not change the number of elements of the Collection using the Collection methods such as add() and remove(), otherwise the structure of the Iterator will be destroyed.

Remove the element using Collection method

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.

Remove the element using Iterator method

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.

⚠️ **GitHub.com Fallback** ⚠️