简体   繁体   中英

How to implement an Iterator that will be an equivalent of a nested loop in Java

I have the following generator in Python :

    def iterator(min,max,step,min2,max2,step2):
        for x in range(min,max,step):
            for y in range(min2, max2, step2):
                result = foo(x, y)
                yield result

And I would like to implement an Iterator in Java that behaves somewhat like the previous generator. I have tried to use two internal iterators, but it does not work.

How can I fix it?

    public class Generator implements Iterator<Integer> {    
        private Iterator<Integer> xIterator;
        private Iterator<Integer> yIterator;    
    
        public Generator(int max1, int max2, int min1, int min2, int step1, int step2) {    
            xIterator = range(min1, max1, step1).iterator();
            yIterator = range(min2, max2, step2).iterator();    
        }
    
        @Override
        public Integer next() {
            while (xIterator.hasNext()) {
                xval = xIterator.next()
                while(yIterator.hasNext()) {
                    yval = yIterator.next()
                    return foo(xval, yval)
                }
            }
        }    
    
        public static int[] range(int min, int max, int step) {
            return IntStream.range(min, max / step + 1).map(x -> x * step).toArray();
        }    
    }

In Python, after a yield statement, the next call to the Generator continues after that yield statement - which is within the inner loop.

In Java, after the return, the next call to next() will continue outside of the while loops, so the first thing it does is always to check xIterator.hasNext(), and if this is true, increment xval. I think this might be the main misunderstanding.

It also seems that the range function doesn't do what it is supposed to do. Maybe check Java: Equivalent of Python's range(int, int)? - some of the answers there include the step argument as well.

The code you posted will also not compile, for several reasons:

  • next() doesn't always return a value. If no (further) elements exist, it should throw a NoSuchElementException.
  • hasNext() is not implemented, and this could actually be quite hard with this approach.
  • xval and yval are not declared.

An Iterator in Java is a special object, a mean of iteration , that allows to retrieve elements sequentially one by one from a particular source.

There are two method that are mandatory to implement while creating a custom iterator: hasNext() (returns true if the next element exists) and next() (retrieves the next element).

You didn't provide your class with implementation of hasNext() without that your code will not compile.

And there's a logical flaw in the next() method, it will not compile because you didn't provide the return statement or throws clause that will get executed when control cant enter the loop. But more important you don't need loops and any conditional logic inside this method, it has to be covered by the hasNext() which normally has to be invoked before next() . If the client code doesn't respect it, method next() might produce an exception. You might add if (hasNext()) at the very beginning of the next() method to issue a particular exception with your custom message.

Method iterator() is accessible with arrays. You can use it with classes that implement Iterable interface, like collections, and you also can invoke iterator() on a stream. So you can reimplement your method range() like that:

IntStream.iterate(min, i -> i < max, i -> i + step).iterator();

And that is how you can fix your iterator:

public class Generator implements Iterator<Integer> {    
    private final Iterator<Integer> xIterator;
    private final Iterator<Integer> yIterator;
    
    public Generator(int minX, int minY, int maxX, int maxY, int stepX, int stepY) {
        this.xIterator = range(minX, maxX, stepX);
        this.yIterator = range(minY, maxY, stepY);
    }
    
    public static Iterator<Integer> range(int min, int max, int step) {
        return IntStream.iterate(min, i -> i < max, i -> i + step).iterator();
    }
    
    @Override
    public boolean hasNext() {
        return xIterator.hasNext() && yIterator.hasNext();
    }
    
    @Override
    public Integer next() {
        return foo(xIterator.next(), yIterator.next());
    }
}

But my suggestion is to favor efficiency and simplicity over conciseness. Because all the values that iterator produces can be easily calculated on the fly, there's no need to occupy memory by allocating them in advance.

Instead, you can maintain two variables curX and curY . This solution is simple and also gives more control over your iterator because you're not delegating the process of iteration. As a consequence of this, you can implement a reset() feature (which is not possible which previous solution, Iterator becomes useless when it reaches the end of the source of data).

public class Generator implements Iterator<Integer> {
    private final int minX;
    private final int minY;
    private final int maxX;
    private final int maxY;
    private final int stepX;
    private final int stepY;
    
    private int curX;
    private int curY;
    
    public Generator(int minX, int minY, int maxX, int maxY, int stepX, int stepY) {
        this.minX = minX;
        this.minY = minY;
        this.maxX = maxX;
        this.maxY = maxY;
        this.stepX = stepX;
        this.stepY = stepY;
        this.curX = minX;
        this.curY = minY;
    }
    
    @Override
    public boolean hasNext() {
        return curX < maxX && curY < maxY;
    }
    
    @Override
    public Integer next() {
        int result = foo(curX, curY);
        curX += stepX;
        curY += stepY;
        return result;
    }
    
    public void reset() { // reset the iterator to the initial coordinates
        this.curX = minX;
        this.curY = minY;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM