在C#中,IEnumerator接口定义了遍历集合并查看元素的方法。我认为这非常有用,因为如果你将IEnumerable<T>
传递给方法,它就不会修改原始来源。
但是,在Java中,Iterator定义remove操作(可选!)允许删除元素。将Iterable<T>
传递给方法没有任何好处,因为该方法仍然可以修改原始集合。
remove
的可选性是refused bequest气味的一个例子,但忽略了(已经讨论过here)我对设计感兴趣促使remove
事件在接口上实现的决定。
导致remove
被添加到Iterator
的设计决策是什么?
换句话说,在remove
上明确没有IEnumerator
定义的C#设计决策是什么?
答案 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)
删除问题。它似乎通过以First
和Last
属性的形式公开列表的内部 ...来实现这一点,其值为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我并不需要所有这些,因为当我在一个项目上时我可以删除它,但迭代将继续!老实说,虽然看起来不合适,但它在很多方面都是一种优化。