2012-07-25 17 views
12

In C# definiert die Schnittstelle IEnumerator eine Möglichkeit, eine Sammlung zu durchlaufen und sich die Elemente anzusehen. Ich denke, das ist enorm nützlich, denn wenn Sie IEnumerable<T> an eine Methode übergeben, wird es nicht die ursprüngliche Quelle ändern.Warum definiert Iterator die Operation remove()?

In Java, Iterator definiert jedoch die remove Operation (optional!) Ermöglichen das Löschen von Elementen. Es hat keinen Vorteil, Iterable<T> an eine Methode zu übergeben, da diese Methode die ursprüngliche Sammlung noch ändern kann.

remove ‚s optionalness ist ein Beispiel für die refused bequest Geruch, aber ignorieren, dass (bereits here diskutiert) ich in den Design-Entscheidungen interessiert sein würde, die eine remove Ereignis aufgefordert, an der Schnittstelle implementiert werden.

Welche Designentscheidungen führten dazu, dass remove zu Iterator hinzugefügt wurde?

Anders gesagt, was ist die C# -Entwurfsentscheidung, die remove explizit nicht definiert ist, auf IEnumerator?

Antwort

8

Iterator kann Elemente während der Iteration entfernen. Sie können die Auflistung nicht mithilfe von Iterator iterieren und Elemente aus der Zielsammlung mithilfe der remove()-Methode dieser Sammlung entfernen. Sie erhalten ConcurrentModificationException beim nächsten Aufruf von Iterator.next(), weil der Iterator nicht wissen kann, wie genau die Sammlung geändert wurde, und nicht wissen kann, wie die Iteration fortgesetzt wird.

Wenn Sie remove() Iterator verwenden, weiß es, wie die Auflistung geändert wurde. Außerdem können Sie eigentlich kein Element der Sammlung entfernen, sondern nur das aktuelle. Dies vereinfacht die Fortsetzung der Iteration.

In Bezug auf die Vorteile der Weitergabe von Iterator oder Iterable: Sie können immer Collection.unmodifireableSet() oder Collection.unmodifireableList() verwenden, um Änderungen an Ihrer Sammlung zu verhindern.

+0

'unmodifizierbarXYZ' ist nur eine Laufzeit Sache, ich mag statische Invarianten. Ich war mehr an der Designentscheidung interessiert, die dazu führte, dass 'remove' hinzugefügt wurde und vielleicht, warum C# auch nicht wählte. –

+0

Die schlimmste Sache, 'unmodiableXYZ' zu verwenden, ist, dass wenn jemand eine XYZ-Datei übergeben hat, die bereits verpackt ist, können Sie sie zweimal umbrechen, und Sie haben keine Möglichkeit, sie zu vermeiden, es sei denn, Sie verwenden Reflektion. Obwohl ich der Meinung bin, dass die Implementierung dies optimieren würde, sieht es für mich immer noch zu schlecht aus. –

+0

Mit Blick auf die OpenJDK-Implementierung scheint es kein mehrfaches Wrapping zu vermeiden. Das bedeutet, dass Sie in einer komplizierten Anwendung eine einfache 'ArrayList ' für 100 Mal oder mehr verpacken können. –

1

Es gibt Situationen, in denen Sie Elemente mithilfe des Iterators entfernen können, da dies der effizienteste Weg ist. Wenn beispielsweise eine verknüpfte Datenstruktur (z. B. eine verkettete Liste) durchlaufen wird, ist das Entfernen unter Verwendung des Iterators eine Operation O(1) ... im Vergleich zu O(N) über die Operationen List.remove().

Und natürlich viele Sammlungen sind so ausgelegt, dass durch andere Mittel als Iterator.remove() während einer Sammlung die Sammlung Modifizierung in einem ConcurrentModificationException führen.


Wenn Sie eine Situation, wo Sie nicht wollen, Modifikation über eine Sammlung Iterator ermöglichen, ist es Collection.unmodifiableXxxx mit Verpackung und die Verwendung dieser Iterator ist die gewünschte Wirkung haben. Alternativ denke ich, dass Apache Commons einen einfachen nicht änderbaren Iterator-Wrapper bietet.


Durch die Art und Weise IEnumerable leidet unter dem gleichen "Geruch", wie Iterator. Schauen Sie sich die reset() Methode an. Ich war auch neugierig, wie die C# LinkedList Klasse mit dem O(N) Entfernung Problem zu tun hat. Es scheint, dass es dies durch das Aussetzen der Interna der Liste ... tutin Form der First und Last Eigenschaften, deren Werte sind LinkedListNode Referenzen. Das verstößt gegen ein anderes Konstruktionsprinzip ... und ist (IMO) weitaus gefährlicher als Iterator.remove().

+0

Mir ist klar, dass "Reset" ist der gleiche Geruch, aber (wie ich in der Frage zu sagen versuchte), ich bin nicht wirklich daran interessiert. 'reset' erlaubt niemandem den Inhalt des Containers zu löschen. Mein Hauptpunkt der Behauptung ist, dass C# iterable die Sammlung in der gleichen Weise verlassen wird, egal was die Methode damit macht (ohne Rücksicht auf die Reflexion). –

+0

@ JeffFoster - wie gesagt, Java-Iteratoren bieten diese Garantie nicht ... aber wenn Sie es wollen, gibt es einfache und kostengünstige Möglichkeiten, dies zu erreichen. –

+0

yup, ich möchte nur, warum sie diese Garantie nicht bieten. Welche Kompromisse hat Java/C# auf der Bibliotheksdesignebene gemacht, um 'remove' zu ​​unterstützen (oder nicht zu unterstützen). –

2

Es ist wahrscheinlich aufgrund der Tatsache, dass das Entfernen von Elementen aus einer Sammlung beim Iterieren immer eine Ursache für Fehler und seltsames Verhalten war. Aus dem Lesen der Dokumentation würde man ersehen, dass Java zur Laufzeit erzwingt. Remove() wird nur einmal pro Aufruf von next() aufgerufen, was mich denken lässt, dass es gerade hinzugefügt wurde, um zu verhindern, dass Leute Daten aus einer Liste entfernen, wenn sie darüber iterieren.

-1

Dies ist eigentlich eine tolle Funktion von Java. Wie Sie vielleicht wissen, gibt es beim Durchlaufen einer Liste in .NET zum Entfernen von Elementen (für die es eine Reihe von Anwendungsfällen gibt) nur zwei Optionen.

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

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

oder

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

, ziehe ich die zweite, aber mit Java ich nicht all das brauchen, weil, während ich auf einen Artikel, bin ich es entfernen kann und dennoch die Iteration wird fortgesetzt! Ehrlich gesagt, obwohl es fehl am Platz scheint, ist es wirklich eine Optimierung in vielerlei Hinsicht.

+0

Im Gegenteil, ich sehe es als abscheulich. Es hält mich davon ab, eine Reihe von Elementen an eine Methode zu übergeben, ohne zu wissen, ob die Methode auf meinen Container anstößt :) Ich bin an der Designentscheidung interessiert, die dazu geführt hat, dass sie hinzugefügt wurde, und insbesondere, warum sie nicht in C# ist. –

+0

Folgen Sie dem Beitrag von StephenC, um zu verhindern, dass Sie Ihren Container toasten - aber ich denke, Sie stellen diese Frage an die falsche Stelle, wenn Sie herausfinden wollen, warum die Java-Designer das implementiert haben und warum Microsoft dies nicht getan hat. Weißt Du, was ich meine? Aber bedenke das Obige und denke darüber nach, wie viel effizienter das ist. Ist das eine Methode, die Sie steuern, oder ist es eine von einem Framework, das Sie konsumieren? –

+0

Ihr Beispiel ist völlig falsch.Sowohl C# als auch Java stellen 'RemoveAt'-Methode für' List 'bereit. Ihr Java-Code funktioniert also auch für C#. Wenn Ihr C# -Code 'foreach'-Struktur verwendet, sollten Sie' for (:) 'auch in Java verwenden. Und Sie werden sehen, dass Sie die 'remove' Methode in einer solchen Schleife nicht wirklich aufrufen können (da Sie keinen expliziten Verweis auf den' Iterator' haben), so dass Ihr "Vorteil" von Java verschwunden ist. –