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



对于我目前的大学课程,我们需要为不可变的数据类型实现一个迭代器。到目前为止还不错,没有什么太难的,下面是它的样子:

public Iterator<T> iterator(){
return new Iterator<T>() {
private int index = 0;
@Override
public boolean hasNext() {
//hasNext implementation
}
@Override
public T next() {
//Next Implementation
}
};

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

如果您需要使每个成员变量final,则可以index生成一个数组:

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

因为数组元素仍然是可变的,即使对它的引用是final。但这只会使代码更加混乱,因为到处都使用index[0],而不仅仅是index

迭代程序本质上是可变的;我想不出让它们变为不可变的,因为您希望在使用它们时进行更改。


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

注意:我对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());
}
}

您还可以编写一个方法来将任何此类迭代器包装为普通迭代器,这样它就可以用于每个循环等等:

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

我来到这里时想到了这个想法,然后决定对它进行研究——但这是迄今为止我发现的最相关的一页,所以我想我应该把我的想法写下来。如果有人知道类似的事情,我很想知道。

最新更新