The Iterator and Iterable
First let’s look at the common traversal
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(3);
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}
System.out.println();
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.print(it.next() + ",");
}
System.out.println();
for (Integer i : list) {
System.out.print(i + ",");
}
System.out.println();
list.forEach(line -> {
System.out.print(line + ",");
});
Copy the code
The first is a regular for loop
The second is iterator traversal
The third is to enhance the for loop
The fourth is the for each loop
The latter two methods refer to the Iterator and iterable interfaces in Java. Let’s take a look at the differences between the two interfaces and how to implement the for each loop in a custom class.
Iterator
Before we begin, let’s look at the definition of the Iterator interface
public interface Iterator<E> {
boolean hasNext(a);
E next(a);
default void remove(a) {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
Copy the code
Iterator uses next and hasNext() to define methods for iterating over collections. The implementation depends on the implementation class, which implements the iterator interface’s methods for iterating.
The significance of the Iterator
One of the biggest convenience features of the Java language, the enhanced for loop, is added to Java 5, removing the last barrier to using Java collections. Previously, developers had to get an Iterator manually, use next() to get the object that Iterator pointed to, and check hasNext() to see if there were any more objects available. Starting with Java 5, we’re free to use a variant of the for loop that handles all of this work behind the scenes. In fact, this enhancement applies to any object that implements the Iterable interface, not just Collections.
No doubt Java developers love the Java collection Iterator, but when was the last time you used the Iterator interface? Suffice it to say, most of the time we just throw iterators into the for() loop or reinforce the for() loop and move on.
But a closer look reveals that Iterator actually has two very useful features.
First, Iterator supports safely removing objects from the source collection by calling remove() on Iterator. The advantage is to avoid ConcurrentModifiedException, this exception as the name implies: when open the Iterator iterative collection, at the same time, in the collection. Some collections do not allow elements to be removed or added during iteration, but it is safe to call Iterator’s remove() method.
Second, Iterator supports derived (and possibly more powerful) sibling members. ListIterator, which exists only in a List, supports adding or removing elements to a List during an iteration, and can scroll through the List in both directions.
Two-way scrolling is particularly useful, especially in the ubiquitous “sliding result set” operation, where only 10 of the many results retrieved from a database or other collection can be displayed. It can also be used to “backward traverse” collections or lists, rather than walking backwards and forwards each time. Inserting a ListIterator is much easier than “walking backwards” through a List with the down-counting integer argument list.get ().
All Java collections contain the iterator() method, which returns an instance of iterator that iterates through the elements of the collection
The method of the Iterator
The Iterator interface provides four methods for manipulating collection elements
hasNext()
– returntrue
If I have something else in my setnext()
– Returns the next element in the collectionremove()
– Deletes the next element in the collection and returns it using the next methodforEachRemaining()
– Operates on the remaining elements in the collection
An example of the Iterator
public class IteratorDemo {
@Test
public void test(a) {
List<String> s = Arrays.asList("a"."b"."c");
Iterator<String> iterator = s.iterator();
iterator.next();
iterator.forEachRemaining((x)-> System.out.println(x));
}
@Test
public void test2(a) {
// Creating an ArrayList
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(3);
numbers.add(2);
System.out.println("ArrayList: " + numbers);
// Creating an instance of Iterator
Iterator<Integer> iterate = numbers.iterator();
// Using the next() method
int number = iterate.next();
System.out.println("Accessed Element: " + number);
// Using the remove() method
iterate.remove();
System.out.println("Removed Element: " + number);
System.out.print("Updated ArrayList: ");
// Using the hasNext() method
while(iterate.hasNext()) {
// Using the forEachRemaining() method
iterate.forEachRemaining((value) -> System.out.print(value + ",")); }}}Copy the code
Output
ArrayList: [1, 3, 2]
Acessed Element: 1
Removed Element: 1
Updated ArrayList: 3, 2,
Copy the code
Notice the following notation in the example above
iterate.forEachRemaining((value) -> System.put.print(value + ", "));
Copy the code
Here we pass a lambda expression as an argument to the forEachRemaining method
可迭代
We notice that when we use Iterator, we usually get an Iterator by calling the Iterator() method, which is defined in the Iterable interface
Let’s first look at the description of this interface definition
/**
* Implementing this interface allows an object to be the target of the "for-each loop" statement. See
* @param <T> the type of elements returned by the iterator
*
* @since 1.5
* @jls 14.14.2 The enhanced for statement
*/
public interface 可迭代<T> {
Iterator<T> iterator(a);
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) { action.accept(t); }}default Spliterator<T> spliterator(a) {
return Spliterators.spliteratorUnknownSize(iterator(), 0); }}Copy the code
This interface defines we can use the for-each this traversal way to traverse the object inside the source code of the Iterable interface elements can be found that its just return an Iterator object.
@Test
public void test(a) {
List<String> s = Arrays.asList("a"."b"."c");
s.forEach(line -> System.out.println(line));
}
Copy the code
Iterable relationship with Iterator
The question is, why not just put the hasNext() and next() methods in the Iterable interface and implement the other classes? But by doing it separately
The reason for this is that some set classes may have more than one traversal mode. Classes that implement Iterable can implement multiple Iterator inner classes. For example, ListItr and DescendingIterator in LinkedList implement bidirectional traversal and reverse traversal respectively. It is more flexible to implement different traversal methods by returning different iterators. If you merge the two interfaces, you can’t return different Iterator implementation classes
The LinkedList ListItr
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext(a) {
return nextIndex < size;
}
public E next(a) {
checkForComodification();
if(! hasNext())throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
returnlastReturned.item; }}Copy the code
conclusion
- There are four ways you can traverse a collection in Java
- The Iterator interface allows us to traverse collections as iterators
- The Iterable interface allows us to iterate over collections using for-each and enhanced for loops, both of which also rely on Iterator iterators, but Java provides syntax-sugar, The Java compiler converts this to Iterator traversal
- The separation of Iterable and Iterator is implemented so that different implementation classes can implement different Iterators more flexibly