Try to make a peepable iterator

Preface

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.

Main subject

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

Implementation

Method configuration

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() { ... }
}

Field composition

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?

Method implementation

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; 
}
** Overall code **

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;
    }
}

Usage problems

Problems caused by not monopolizing the iterator

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

The problem that an exception occurs when peek () is done at the end point

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

Existing library

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

Postscript

That's it.

[^ 1]: I haven't investigated it so seriously, so if I google it unexpectedly, it may come out soon.

Recommended Posts

Try to make a peepable iterator
Try to make a simple callback
Try to make a music player using Basic Player
How to make a Java container
[Beginner] Try to make a simple RPG game with Java ①
How to make a splash screen
How to make a Maven project
Try to create a server-client app
CompletableFuture Getting Started 2 (Try to make CompletableFuture)
How to make a Java array
Try to make a CS 3D tile from the Geographical Survey tile
How to make a Discord bot (Java)
Make a margin to the left of the TextField
Try to create a bulletin board in Java
Increment behavior Try to make Java problem TypeScript 3-4
I wanted to make (a == 1 && a == 2 && a == 3) true in Java
String operation Try to make Java problem TypeScript 9-3
How to make a lightweight JRE for distribution
Try to make a timeline report of method execution time using JFR API
[Introduction] Try to create a Ruby on Rails application
Try to release gem
Initialization of for Try to make Java problem TypeScript 5-4
How to make JavaScript work on a specific page
Try to make an addition program in several languages
I tried to make a login function in Java
How to make shaded-jar
Make a reflection utility ②
Make a reflection utility ③
Try to solve a restricted FizzBuzz problem in Java
How to make a cache without thinking too much
Make a reflection utility ①
How to make a mod for Slay the Spire
Try to build a Java development environment using Docker
Try sending a notification.
[Introduction to Android application development] Let's make a counter
A memo to check when you try to use Lombok
I want to make a specific model of ActiveRecord ReadOnly
I want to make a list with kotlin and java!
I just wanted to make a Reactive Property in Java
I want to make a function with kotlin and java!
Learning Ruby with AtCoder 13 How to make a two-dimensional array
I tried using Hotwire to make Rails 6.1 scaffold a SPA
I tried to make a client of RESAS-API in Java
Try Spring Boot from 0 to 100.
How to leave a comment
To make heavy queries asynchronously
Pass a variable to Scope.
[Java] Make it a constant
Try implementing a WebFlux session
To write a user-oriented program (1)
[Rails] Make a breadcrumb trail
Introduction to Design Patterns (Iterator)
Try implementing a WebFlux filter
[Rails] How to make seed
How to insert a video
Make a rhombus using Java
How to create a method
Try to imitate the idea of a two-dimensional array with a one-dimensional array
How to make a hinadan for a Spring Boot project using SPRING INITIALIZR
How to make a jar file with no dependencies in Maven
[Unity] I tried to make a native plug-in UniNWPathMonitor using NWPathMonitor