如何实现相当于 Java 中的嵌套循环的迭代器

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

我在 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

我想在 Java 中实现一个 Iterator ,它的行为有点像以前的生成器。我试过使用两个内部迭代器,但它不起作用。

我该如何解决?

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

在 Python 中,在 yield 语句之后,对 Generator 的下一次调用在该 yield 语句之后继续 - 这是在内循环中。

在 Java 中,在 return 之后,对 next() 的下一次调用将在 while 循环之外继续,所以它做的第一件事总是检查 xIterator.hasNext (),如果为真,则递增 xval。我认为这可能是主要的误解。

范围函数似乎也没有做它应该做的事情。也许检查 Java: Equivalent of Python's range(int, int)? - 那里的一些答案也包括 step 参数。

您发布的代码也无法编译,原因如下:

  • next() 并不总是 return 一个值。如果不存在(更多)元素,它应该抛出 NoSuchElementException。
  • hasNext() 未实现,使用这种方法实际上可能非常困难。
  • xval 和 yval 未声明。

Java 中的 迭代器 是一个特殊的对象,迭代的平均值,它允许按顺序检索元素来自特定来源的一个。

创建自定义迭代器时有两种方法必须实现:hasNext()(returns true 如果下一个元素存在)和next() (检索下一个元素)。

您没有为 class 提供 hasNext() 的实现,否则您的代码将无法编译。

并且next()方法有一个逻辑缺陷,它不会编译,因为你没有提供return语句或throws子句,当控制无法进入循环时将被执行。但更重要的是,您不需要此方法中的循环和任何条件逻辑,它必须被通常必须在 next() 之前调用的 hasNext() 覆盖。如果客户端代码不遵守它,方法 next() 可能会产生异常。您可以在 next() 方法的最开头添加 if (hasNext()) 以针对您的自定义消息发出特定异常。

方法 iterator() 可通过数组访问。您可以将它与实现 Iterable 接口的 classes 一起使用,例如集合,您还可以在流上调用 iterator()。所以你可以像这样重新实现你的方法range()

IntStream.iterate(min, i -> i < max, i -> i + step).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());
    }
}

但我的建议是更注重效率和简洁而不是简洁。因为迭代器产生的所有值都可以很容易地动态计算,所以不需要预先分配它们来占用内存。

相反,您可以维护两个变量 curXcurY。这个解决方案很简单,而且还可以更好地控制迭代器,因为您没有委托迭代过程。因此,您可以实现 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;
    }
}