When creating a wrapper class for an iterator, it is often difficult to implement hasNext. If you just want to wrap it, you can delegate it to an internal iterator, but if you can't take such a strategy for convenience.
It would be convenient if I could take a peek at the next element ... So, let's make it.
I made PeekableIterator, a class that works as follows.
var src = java.util.List.of(3, 1, 4);
var peekIt = new PeekableIterator<>(src);
while(peekIt.hasNext()) {
System.out.printf("The next element is%d.\n", peekIt.peek());
System.out.printf("I will say it again.%d.\n", peekIt.peek());
System.out.printf("You see, really%Was it d?\n", peekIt.next());
System.out.println();
}
System.out.println("The end");
Execution result
The next element is 3.
I will say it again. It is 3.
You see, it was really 3, right?
The next element is 1.
I will say it again. It is 1.
You see, it was really 1, right?
The next element is 4.
I will say it again. It is 4.
You see, it was really 4, right?
The end
It is a simple configuration with only the constructor and the favorite method peek () added. When hasNext () is false, peek () throws exception NoSuch ~. This traces the specification of Iterator # next ().
PeekableIterator.java
import java.util.Iterator;
import java.util.NoSuchElementException;
public class PeekableIterator<T> implements Iterator<T> {
public PeekableIterator(Iterable<T> iterable) { ... }
public PeekableIterator(Iterator<T> it) { ... }
public T peek() throws NoSuchElementException { ... }
@Override
public T next() throws NoSuchElementException { ... }
@Override
public boolean hasNext() { ... }
}
The configuration is as follows. Considering the possibility that it.next () returns null, nextElem and hasNext are handled as a set.
private final Iterator<T> it; //Omoto Iterator
private T nextElem; //Next element
private boolean hasNext = false; //Do the following elements exist?
The constructor takes a peek and holds the next element in the field.
PeekableIterator#new
public PeekableIterator(Iterable<T> iterable) {
this(iterable.iterator());
}
public PeekableIterator(Iterator<T> it) {
this.it = it;
if(it.hasNext()) {
nextElem = it.next();
hasNext = true;
}
}
The method hasNext () is, after all, just a getter. next () only returns nextElem, but it needs some processing to peep into the next element.
PeekableIterator#hasNext,#next
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() throws NoSuchElementException {
if(!hasNext()) {
throw new NoSuchElementException();
}
final T ret = nextElem;
if(it.hasNext()) { nextElem = it.next(); }
else { hasNext = false; }
return ret;
}
Implementation of favorite peek () is also easy. However, you have to check if nextElem is invalid.
Java:PeekableIterator::peek
public T peek() throws NoSuchElementException {
if(!hasNext()) {
throw new NoSuchElementException();
}
return nextElem;
}
Peekable.java
import java.util.Iterator;
import java.util.NoSuchElementException;
public class PeekableIterator<T> implements Iterator<T> {
//
private final Iterator<T> it;
private T nextElem;
private boolean hasNext = false;
public PeekableIterator(Iterable<T> iterable) {
this(iterable.iterator());
}
public PeekableIterator(Iterator<T> it) {
this.it = it;
if (it.hasNext()) {
nextElem = it.next();
hasNext = true;
}
}
//
public T peek() throws NoSuchElementException {
if (!hasNext()) {
throw new NoSuchElementException();
}
return nextElem;
}
//
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() throws NoSuchElementException {
if (!hasNext()) {
throw new NoSuchElementException();
}
final T ret = nextElem;
if (it.hasNext()) {
nextElem = it.next();
} else {
hasNext = false;
}
return ret;
}
}
When the iterator is received by the constructor, it is not duplicated. Therefore, if the caller directly operates the iterator, the element will appear to be skipped.
var it = java.util.List.of(3, 1, 4).iterator();
var peekIt = new itertools.PeekableIterator<>(it);
it.next();
while(peekIt.hasNext()) {
System.out.printf("The next element is%d.\n", peekIt.peek());
System.out.printf("I will say it again.%d.\n", peekIt.peek());
System.out.printf("You see, really%Was it d?\n", peekIt.next());
System.out.println();
}
System.out.println("The end");
Execution result
The next element is 3.
I will say it again. It is 3.
You see, it was really 3, right?
The next element is 4.
I will say it again. It is 4.
You see, it was really 4, right?
The end
However, it seems impossible to make the iterator completely independent.
--The iterator is, so to speak, a "state", and it depends on the provider whether it can be duplicated as it is. --You can create as many iterators as you like by reading all the elements, but the merit of delaying the acquisition of elements disappears. --In the first place, when all the elements are read, the original iterator withers. ――What about an iterator that returns elements infinitely in the first place?
It would be nice if there was a mechanism to give the iterator the "unavailable" attribute ...
Although it is as specified.
var src = java.util.List.of(3, 1);
var peekIt = new PeekableIterator<>(src);
while(peekIt.hasNext()) {
System.out.printf("The current element is%d.\n", peekIt.next());
System.out.printf("The next element is%d. looking forward to.\n", peekIt.peek());
System.out.println();
}
System.out.println("The end");
Execution result
The current element is 3.
The next element is 1. looking forward to.
The current element is 1.
Exception in thread "main" java.util.NoSuchElementException
at ...
When deciding the interface of this program, I referred to more_itertools.peekable of Python.
However, when I look it up again, it seems that Java also provides a similar class. Let's actually use it while paying attention to how the end points are processed.
Apache Commons: Class PeekingIterator<E>
You can also get an instance with peekingIterator (), but the difference with the constructor is unknown. [^ 1] Element () and peek () can be used properly according to the purpose. Convenient for soberness.
var src = java.util.List.of(3, 1);
var peekIt = new PeekingIterator<>(src.iterator());
while(peekIt.hasNext()) {
System.out.printf("The current element is%d.\n", peekIt.next());
System.out.printf("The next element is%d. looking forward to.(peek)\n", peekIt.peek());
System.out.printf("The next element is%d. looking forward to.(element)\n", peekIt.element());
System.out.println();
}
System.out.println("The end");
Execution result
The current element is 3.
The next element is 1. looking forward to.(peek)
The next element is 1. looking forward to.(element)
The current element is 1.
The next element is null. looking forward to.(peek)
Exception in thread "main" java.util.NoSuchElementException
at ...
Guava: Interface PeekingIterator<E>
Receiving instances via Iterators.peekingIterator become. There is no element () method here, and the terminating peek () seems to end with an exception.
var src = java.util.List.of(3, 1);
var peekIt = Iterators.peekingIterator(src.iterator());
while(peekIt.hasNext()) {
System.out.printf("The current element is%d.\n", peekIt.next());
System.out.printf("The next element is%d. looking forward to.\n", peekIt.peek());
System.out.println();
}
System.out.println("The end");
The current element is 3.
The next element is 1. looking forward to.
The current element is 1.
Exception in thread "main" java.util.NoSuchElementException
at ...
That's it.
[^ 1]: I haven't investigated it so seriously, so if I google it unexpectedly, it may come out soon.
Recommended Posts