简体   繁体   English

如何实现一个等效于 Java 中的嵌套循环的迭代器

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

I have the following generator in Python :我在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.我想用Java实现一个Iterator ,它的行为有点像以前的生成器。 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.在 Python 中,在 yield 语句之后,对 Generator 的下一次调用会在该 yield 语句之后继续 - 它位于内部循环中。

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.在 Java 中,在返回之后,对 next() 的下一次调用将在 while 循环之外继续,所以它做的第一件事总是检查 xIterator.hasNext(),如果这是真的,则增加 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)?也许检查Java: Equivalent of Python's range(int, int)? - some of the answers there include the step argument as well. - 那里的一些答案也包括 step 参数。

The code you posted will also not compile, for several reasons:您发布的代码也不会编译,原因如下:

  • next() doesn't always return a value. next() 并不总是返回一个值。 If no (further) elements exist, it should throw a NoSuchElementException.如果不存在(更多)元素,它应该抛出 NoSuchElementException。
  • hasNext() is not implemented, and this could actually be quite hard with this approach. hasNext() 没有实现,使用这种方法实际上可能非常困难。
  • xval and yval are not declared. xval 和 yval 未声明。

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. Java 中的Iterator是一个特殊的对象,是一种迭代的意思,它允许从特定的源一个接一个地顺序检索元素。

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).在创建自定义迭代器时必须实现两种方法: hasNext() (如果存在下一个元素,则返回true )和next() (检索下一个元素)。

You didn't provide your class with implementation of hasNext() without that your code will not compile.您没有为您的类提供hasNext()的实现,否则您的代码将无法编译。

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.而且next()方法有一个逻辑缺陷,它不会编译,因为你没有提供 return 语句或 throws 子句,当控件无法进入循环时将被执行。 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() .但更重要的是,此方法中不需要循环和任何条件逻辑,它必须由hasNext()覆盖,通常必须在next()之前调用。 If the client code doesn't respect it, method next() might produce an exception.如果客户端代码不尊重它,方法next()可能会产生异常。 You might add if (hasNext()) at the very beginning of the next() method to issue a particular exception with your custom message.您可以在next()方法的最开始添加if (hasNext())以使用您的自定义消息发出特定异常。

Method iterator() is accessible with arrays.方法iterator()可通过数组访问。 You can use it with classes that implement Iterable interface, like collections, and you also can invoke iterator() on a stream.您可以将它与实现Iterable接口的类一起使用,例如集合,也可以在流上调用iterator() So you can reimplement your method range() like that:所以你可以像这样重新实现你的方法range()

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 .相反,您可以维护两个变量curXcurY 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).因此,您可以实现一个reset()功能(这在以前的解决方案中是不可能的,当Iterator到达数据源的末尾时它变得无用)。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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