我今年曾是一年级Java编程课程的助教。作为迭代器/迭代器的一个非常简单的示例,我为学生编写了以下代码。我很好奇是否可以进行任何样式或其他改进。

 import java.util.NoSuchElementException;
import java.util.Iterator;

public class Range implements Iterable<Integer> {
    private int start, end;

    public Range(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public Iterator<Integer> iterator() {
        return new RangeIterator();
    }

    // Inner class example
    private class RangeIterator implements
                    Iterator<Integer> {
        private int cursor;

        public RangeIterator() {
            this.cursor = Range.this.start;
        }

        public boolean hasNext() {
            return this.cursor < Range.this.end;
        }

        public Integer next() {
            if(this.hasNext()) {
                int current = cursor;
                cursor ++;
                return current;
            }
            throw new NoSuchElementException();
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }


    public static void main(String[] args) {
        Range range = new Range(1, 10);

        // Long way
        Iterator<Integer> it = range.iterator();
        while(it.hasNext()) {
            int cur = it.next();
            System.out.println(cur);
        }

        // Shorter, nicer way:
        // Read ":" as "in"
        for(Integer cur : range) {
            System.out.println(cur);
        }
    }
}
 


评论

如何@override注释?

本文可能会有所帮助:yegor256.com/2015/04/30/iterating-adapter.html

#1 楼

变量

我理解为什么要使用Range.this.endRange.this.start以避免混淆那些变量的来源...如果您需要Range.this作为教学练习的一部分,那么可以肯定。否则,我会建议三件事。...


添加range作为前缀,即使它有点多余
使它们成为最终的...
每行变量...(使版本控制差异/补丁更易于阅读)

代码如下:

private final int rangeStart;
private final int rangeEnd;


然后,所有Range.this.start都只是rangeStart等。

嵌套类

您的迭代器类是非静态类,因此它可以引用外部类的范围开始end。

在这种情况下,可以很容易地将嵌套类更改为静态类。这有可能简化内存管理,因为迭代器不需要引用封闭的范围。

考虑一个私有静态迭代器实例:

// Inner class example
private static final class RangeIterator implements
                Iterator<Integer> {
    private int cursor;
    private final int end;

    public RangeIterator(int start, int end) {
        this.cursor = start;
        this.end = end;
    }

    public boolean hasNext() {
        return this.cursor < end;
    }

    public Integer next() {
        if(this.hasNext()) {
            int current = cursor;
            cursor ++;
            return current;
        }
        throw new NoSuchElementException();
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}


此静态类完全不需要使用对Range.this的反向引用。 >
预验证

最好预先验证状态,而不是陷入错误...此代码: >
public Iterator<Integer> iterator() {
    return new RangeIterator(start, end);
}



会更好一些:简化为:


    public Integer next() {
        if(this.hasNext()) {
            int current = cursor;
            cursor ++;
            return current;
        }
        throw new NoSuchElementException();
    }



到正好:想象这是一种教育策略。

以整数为例

由于int自动合并,我担心Integer可能不是数据类型的正确选择。您可能希望将非原始数据作为数据。

自动装箱会引起混淆。

结论

我对问题的看法不多。

评论


\ $ \ begingroup \ $
谢谢。我没想到要使用final,这是个好主意。我也同意私有的静态类要比内部类好,但是我忘了提到代码是为了向他们提供内部类的示例,以供进一步了解。后增量是为了避免混淆而进行的更长的方法,因为学生们发现它令人困惑。关于使用非原始类型的有趣观点。为了简单起见,我想提供一个不是容器(例如,链表,哈希集等)的可迭代的示例。您能想到一个不使用原语的东西吗?
\ $ \ endgroup \ $
– Sahand
2014年4月25日下午5:55

\ $ \ begingroup \ $
关于“ Pre-Validate”的要点,要加上一些措辞:这个惯用法被称为“保护条款”(c2.com/cgi/wiki?
\ $ \ endgroup \ $
–mdo
2014年4月25日在6:20

#2 楼

总体而言,这是值得学习的很好的代码。

功能性

我喜欢您分别对下限和上限使用了包容性-排他性约定。该设计的基本原理将是一个有趣的讨论主题。

为了方便起见,我建议添加第二个构造函数:

public Range(int end) {
    this(0, end);
}


start()end()的吸气剂。从技术上讲,您也应该覆盖.equals().hashCode(),但为简单起见也可以忽略它们。做个好习惯。它还可以帮助学生了解哪些方法是界面的强制性部分(嗯,实际上是所有方法)。

JavaDoc将是一个很好的教学习惯。至少要记录外部类和内部类,并可能还要记录它们的构造函数。它们看起来不太像那种函数调用。

@Overrideif声明为for可以帮助向学生强调while是不可变的,只有start会改变状态。也许添加end可以减轻@rolfl对涉及finalRange的内部类的担忧。 />
…虽然我能理解您是否选择不让学生负担那琐事。独立保持状态。也许这可能是一个很好的例子?

    @Override
    public Integer next() {
        if (!this.hasNext()) {
            throw new NoSuchElementException();
        }
        // The post-increment magically takes effect _after_
        // returning.  This is equivalent to
        //
        //     int value = this.cursor++;
        //     return value;
        //
        return this.cursor++;
    }


#3 楼

@rolfl完全钉住了它。只剩下一些挑剔的人了:


我会丢弃所有无意义的注释,除非它们在您教此时有目的。 IDE
每当可以从@Override放下this.时,我都会放下它。使用您的IDE的重新格式化功能(相当于Eclipse中的this.cursor

我认为在向您的班级提出问题之前先问一个好主意!

评论


\ $ \ begingroup \ $
在这里发表评论是因为您提到...评论:我个人更喜欢多行评论,而不是// —并且在我的代码库中,我至少坚持一个类级别的Javadoc(无论如何,Range概念是什么?)
\ $ \ endgroup \ $
–mdo
2014年4月25日在6:27

\ $ \ begingroup \ $
和@Override不仅具有可读性,因为它可以确保正确的覆盖和编译错误
\ $ \ endgroup \ $
–mdo
2014年4月25日在6:29

#4 楼

我认为有些学生会喜欢没有内部类的示例:

范围可以在没有内部类的情况下实现Iterator。您只需要将光标重置为起始值即可。在这里,我完成了对范围的迭代之后,在Iterator方法和下一个方法中重置了游标。它适用于建议的示例。当然,迭代器并不能保持状态独立,也不能用于更复杂的示例,但是我不需要将构造函数参数传递给内部类。

import java.util.NoSuchElementException;
import java.util.Iterator;

public class Range implements Iterable<Integer>, Iterator<Integer> {
    private int start, end, cursor;

    public Range(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public Iterator<Integer> iterator() {
        cursor = start;
        return this;
    }

    public boolean hasNext() {
        return cursor < end;
    }

    public Integer next() {
        if(!hasNext()) {
            cursor = start;
            throw new NoSuchElementException();
        }
        return cursor++;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    public static void main(String[] args) {
        Range range = new Range(1, 10);

        // Long way
        Iterator<Integer> it = range.iterator();
        while(it.hasNext()) {
            int cur = it.next();
            System.out.println(cur);
        }

        // Shorter, nicer way:
        // Read ":" as "in"
        for(Integer cur : range) {
            System.out.println(cur);
        }

        Range digits = new Range(0, 10);
        for (Integer tensDigit : digits) {
            for (Integer onesDigit : digits) {
                System.out.format("%s%s ", tensDigit, onesDigit);
        }
        System.out.println();
        }
    }
}
>

#5 楼

可以使用匿名类编写iterator方法,该类直接访问其工作所需的属性。

@Override
public Iterator<Integer> iterator() {
    return new Iterator<Integer>() {
        private int cursor = start;

        @Override
        public boolean hasNext() {
            return cursor < end;
        }

        @Override
        public Integer next() {
            if (! hasNext()) {
                throw new NoSuchElementException();
            }
            return cursor++;    
        }
    };


,通常需要使用匿名/内部/单独的类。