为不可变数据类型实现迭代器

时间:2017-01-16 12:04:00

标签: java iterator immutability

对于我目前的大学课程,我们需要为不可变数据类型实现Iterator。到目前为止一切都那么好,没什么太糟糕的,这就是它的样子:

public Iterator<T> iterator(){
    return new Iterator<T>() {
        private int index = 0;

        @Override
        public boolean hasNext() {
            //hasNext implementation
        }

        @Override
        public T next() {
            //Next Implementation
        }
    };

我使用了一个可变变量(index),它指向数据类型中的当前位置。练习要求我们将每个成员变量设为final(对于不可变数据类型有意义),但它还要求我们在迭代器final(index)中创建每个成员变量。这让我感到困惑,因为我没有看到在没有可变变量的情况下迭代数据类型的方法,特别是因为你无法从next()方法内部改变迭代器... 我不想要解决这个问题,我只是想知道,这是否可能,也许是一个解决方案的轻微提示......谢谢!

2 个答案:

答案 0 :(得分:2)

如果要求每个成员变量final是您的要求,那么可以index变成数组:

public Iterator<T> iterator(){
    return new Iterator<T>() {
      private final int[] index = {0};
      // ...
    };
}

因为数组元素保持可变,即使对它的引用是final。但这只会使代码更加混乱,因为在所有地方使用index[0]而不仅仅是index

迭代器本质上是可变的;我无法想到让它们变成不可变的,因为你希望在你使用它们时改变它们。

请注意,简单地使所有成员变量final确实使类型不可变。成员变量还必须引用深度不可变的对象;非零长度数组不能完全不可变,因为你总是可以重新分配它们的元素。

答案 1 :(得分:0)

注意:我对Java并不是很熟悉,并且基于我在C#中所做的事情,但我会有所作为。

注意:我现在忽略了“仅提示”请求,因为它现在要晚得多,所以我假设这不再适用。否则,请不要超出第一个代码示例(包括或排除,直至您)。

你可以通过创建一个迭代器类型来解决这个问题,每次移动到下一个元素时都会返回一个新的迭代器对象。因此,基本上,每次移动 - 下一步操作都将返回三件事:

  • 是否有下一个项目(照例)
  • 下一个项目是什么(照例)
  • 一个新的迭代器对象,它表示下一个项目(即当前迭代器的副本,但向前移动一个元素)。

这种设计的优点是你可以在任何时候停止迭代,然后从那一点开始继续 - 而不必首先遍历集合的先前元素。使用.NET LINQ方法,例如Where()或OrderBy(),这可能需要大量处理来再次迭代先前的元素,这在某些情况下可能非常有用。

但是,这种新模式不支持内置功能(例如foreach和LINQ的Java等价物)。此外,迭代器对象应该是一种值类型以提高性能(因为为集合中的每个项创建了一个新实例),但Java不支持自定义值类型。因此,Java模式可能类似于:

interface IterationItem<T> {
    public T getValue();
    public boolean hasNext();
    public IterationItem<T> next();
}

实现如下:

class ListIterationItem<T> implements IterationItem<T>
{
    private List<T> _list; // Could be public (so long as it's read-only)
    private int _index;    // ^
    private T _value;

    public ListIterationItem(List<T> list, int index) {
        _list = list;
        _index = index;
        _value = list[index];
    }

    public T getValue() { return _value; } //Or look up the value on-demand, idk which is better

    public boolean hasNext() {
        return _index + 1 < _list.size())
    }

    public IterationItem<T> next() {
        return new ListIterationItem<T>(_list, _index + 1);
    }
}

您可以这样使用:

List<String> list = Arrays.asList("aaa", "bbb", "ccc");
IterationItem<String> item = new ListIterationItem<String>(list, 0);
while (true) {
    String current = item.getValue();

    //Do something with the current value

    if (item.hasNext()) { item = item.next(); }
};

或者,如果你真的需要避免修改任何字段(例如,当使用不允许你的语言时),你可以使用递归:

void Main() {
    List<String> list = Arrays.asList("aaa", "bbb", "ccc");
    ProcessElements(new ListIterationItem<String>(list, 0));
}
void ProcessElements(IterationItem<T> item) {
    String current = item.getValue();
    //Do something with the current value
    if (item.hasNext()) {
        ProcessElements(item.next());
    }
}

您还可以编写一个方法来将这种迭代器包装为普通迭代器,以便它可以用于for-each循环等等:

public static Iterable<T> AsIterable<T>(IterationItem<T> item)
    return new Iterable<T>() {
        @Override
        public Iterator<T> iterator() {
            return new Iterator<String>() {
                private IterationItem<T> currentItem = item; //Idk if this works

                @Override
                public boolean hasNext() {
                    return item.hasNext();
                }

                @Override
                public String next() {
                    item = item.next();
                    return item.getValue();
                }

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

我考虑到这个想法来到这里,然后决定研究它 - 但这是我到目前为止找到的最相关的页面,所以我想我会记下我的想法。如果有人知道类似的东西,我有兴趣知道。

相关问题