为什么迭代器定义了remove()操作?

时间:2012-07-25 11:10:30

标签: c# java iterator ienumerator

在C#中,IEnumerator接口定义了遍历集合并查看元素的方法。我认为这非常有用,因为如果你将IEnumerable<T>传递给方法,它就不会修改原始来源。

但是,在Java中,Iterator定义remove操作(可选!)允许删除元素。将Iterable<T>传递给方法没有任何好处,因为该方法仍然可以修改原始集合。

remove可选性refused bequest气味的一个例子,但忽略了(已经讨论过here)我对设计感兴趣促使remove事件在接口上实现的决定。

导致remove被添加到Iterator的设计决策是什么?

换句话说,在remove上明确没有IEnumerator定义的C#设计决策是什么?

4 个答案:

答案 0 :(得分:8)

Iterator能够在迭代期间删除元素。您不能使用迭代器迭代集合,并使用该集合的remove()方法从目标集合中删除元素。下次调用ConcurrentModificationException时,您将获得Iterator.next(),因为迭代器无法知道集合的确切变化,也无法知道如何继续迭代。

当您使用迭代器的remove()时,它知道如何更改集合。而且实际上你不能删除任何集合元素但只删除当前元素。这简化了迭代的继续。

关于传递迭代器或Iterable的优点:您始终可以使用Collection.unmodifireableSet()Collection.unmodifireableList()来阻止修改您的集合。

答案 1 :(得分:2)

这可能是因为在迭代过程中从集合中删除项目一直是导致错误和奇怪行为的原因。通过阅读文档,它会建议Java在运行时强制执行remove()仅在每次调用next()时调用一次,这使我认为它刚刚被添加以防止人们在迭代时从列表中删除数据。 / p>

答案 2 :(得分:1)

在某些情况下,您希望能够使用迭代器删除元素,因为这是最有效的方法。例如,当遍历链接数据结构(例如链表)时,使用迭代器删除是O(1)操作...通过O(N)操作与List.remove()进行比较。

当然,许多集合的设计是为了在集合期间通过Iterator.remove()以外的任何其他方式修改集合,这将导致ConcurrentModificationException


如果您不希望允许通过集合迭代器进行修改,则使用Collection.unmodifiableXxxx将其包装并使用它的迭代器将具有所需的效果。或者,我认为Apache Commons提供了一个简单的不可修改的迭代器包装器。


顺便说一下,IEnumerable遭遇与Iterator相同的“嗅觉”。看一下reset()方法。我也很好奇C#LinkedList类如何处理O(N)删除问题。它似乎通过FirstLast属性的形式公开列表的内部 ...来实现这一点,其值为LinkedListNode引用。这违反了另一个设计原则......并且(IMO)比Iterator.remove()危险得多。

答案 3 :(得分:-1)

这实际上是Java的一个很棒的功能。您可能知道,在迭代.NET中的列表以删除元素(其中有许多用例)时,您只有两个选项。

var listToRemove = new List<T>(originalList);
foreach (var item in originalList)
{
    ...
    if (...)
    {
        listToRemove.Add(item)
    }
    ...
}

foreach (var item in listToRemove)
{
    originalList.Remove(item);
}

var iterationList = new List<T>(originalList);
for (int i = 0; i < iterationList.Count; i++)
{
    ...
    if (...)
    {
        originalList.RemoveAt(i);
    }
    ...
}

现在,我更喜欢第二种,但是使用Java我并不需要所有这些,因为当我在一个项目上时我可以删除它,但迭代将继续!老实说,虽然看起来不合适,但它在很多方面都是一种优化。