Schnelles zugreifen auf eine Liste über ein Array oder ein Dictionary?

  • VB.NET

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von jvbsl.

    Schnelles zugreifen auf eine Liste über ein Array oder ein Dictionary?

    Hallo,
    ich verstehe noch nicht ganz, wann es sich lohnt ein Array zu nehmen und wann ich ein Dictionary nutzen sollte. Ich möchte über eine integer ID eines Knotens (die gesamte Anzahl an Knoten ist bekannt und bleibt fix) jeweils schnell auf eine Liste von Knoten zugreifen, die sich im Verlaufe des Algorithmus jedoch immer wieder verändert.Welche Datenstruktur bietet sich da an?
    Dictionary:
    + Du kannst generisch arbeiten (Dictionary(Of Tier) kann x beliebig viele Objekte enhalten, deren Klasse von Tier arbeiten)
    + Bequemer Zugriff per Linq oder auch einfach nur per Index
    + Vergrößert sich selbstständig (Array hat eine feste Größe)
    + Dictionary kannst du besser serialisieren (bei Array musst du das gesamte Array durchlaufen und jeden Wert rausschreiben

    - Da das Dictionary im Prinzip ein Array in einer Klasse kapselt, ist es leicht langsamer
    - Da es ein Array kapselt, muss es bei Bedarf erweitern (Speicher muss kopiert werden => dauert lange)
    - Da es eine Klasse ist, braucht es etwas mehr Speicherplatz, da die Klasse drumherum einen (kleinen) Overhead verursacht


    Array:
    + Sind Schnell, da man "nur" per Index drauf zugreifen kann, gibt zwar Linq, aber is (IMHO) ded wirklich so der Burner
    + Sind platzsparender als Dicionary, da keine extra Klasse - außer man verwendet System.Array
    + (mehr fällt mir jetzt ned dazu ein)
    - Da Array nur einen Wert pro Feld speichern kann, kannst du keine Schlüssel-Wert auflistung machen
    - Ein Array ist von der Bedienbarkeit (IMHO) nicht ganz so gut, wie die Listen oder Dictionary, da es weniger Zugriffsmöglichkeiten bietet


    Um deine Frage zu beantworten:
    Arrays sind in Fällen, in OOP-Sprachen zur heutigen Zeit eigentlich nicht mehr so in Verwendung, stattdessen bieten ja (fast) alle Sprachen und ihre Framworks sogenannte Generics bzw Template (C++, ist aber das selbe). Die kapsen auch ein Array, nur eben im OOP-Style. Diese Listen werden - wie der Name bereits verrät - verwendet, wenn "einfache" Daten zu verwalten sind, also Namen oder z.b in einer Anwendung mehrer Controls sind und du nicht für jedes Control einen eigene Eventhandler Prozedur schreiben willst, dann kannst du einfach eine Liste erstellen, in die packst du die Controls rein und weißt derem Events einen Eventhandler zu (Falls ich hier Nonsens erzähle, bitte korrigieren)
    Dictionary werden hingegen bei Schlüssel-Wert-Auflistungen verwendet - wie in deinem Fall. Dazu musst du wissen, dass das Dictionary intern auch mit einen Array aufgebaut ist, welches vom Typ KeyValuePair(Of T1, T2) ist. KeyValuePair ist einen Struktur mit 2 Attributen (Properties) vom Typ T1 und T2 (Generische Typen - heißt, du kannst für T1 und T2 2 komplett verschiedene Typen verwenden (String und Integer, aber auch Integer und eine beliebige Klasse). Dicionaries kannst du verwenden, wenn du bspw eine Zuordung brauchst zwischen 2 Werten (gemeint sind hier sowohl Werte als auch Objekte) brauchst. Etwa in einer Anwendung, die einen Chat darstellt, hier kannst du entweder eine List(Of Connection) oder ein Dictionary(Of String, Of User) machen. Für die erste Variante eine Klasse Connection bauen, in der du unter anderem den Namen des Users reinpackst, dann kannst du einen bestimmten Eintrag per .Find(Predicate) finden. Im zweiten Fall ist es noch einfacher, hier ist das Finden durch den Schlüssel String vereinfacht. Dazu eine Klasse User bauen, die den User kapselt, für den String als Key verwendest du dann den Usernamen

    Ich hoffe ich konnte helfen

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    ich würd Array nicht ganz abschreiben.
    Wie du sagst: Es ist das schnellste, wasses gibt, und hat den geringsten Overhead.
    Und es ist ja eigentlich auch eine generische Auflistung - im Grunde sogar die Ur-generische Auflistung.

    Und wenn die Knoten-Anzahl fix ist, ists auch nicht dramatisch, dass man einem Array nix zufügen kann.
    Man muss auch sehen, dass auch Dictionary einen Nachteil hat: Man kann es nicht sortieren, oder sonstwie von einer Reihenfolge der Elemente ausgehen.

    ErfinderDesRades schrieb:

    Dictionary einen Nachteil hat: Man kann es nicht sortieren
    @ErfinderDesRades: Danke für die Ergänzung :D Aber Dafür gibt es SortedDicionary
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    ja, aber das kann nur nach Schlüssel sortiert sein (was eiglich auch das sinnvollste ist).
    Aber dabei darf kein Schlüssel doppelt vorkommen - während bei einem (sortierten) Array Doubletten kein Problem sind.
    Beinem Array könnte man sogar den Sortier-Schlüssel ändern.

    Egal - ein Dic ist halt was anneres als eine typisierte Auflistung (wie List(Of T) oder T-Array)
    Und braucht man denn auch für was anneres.
    Ohne mich näher einbringen zu wollen, der Hauptunterschied zwischen einem Array (oder Liste) und einem Dictionary ist das Speicherformat bzw. das Ansprechen bestimmter Einträge.
    Ein Array ist eine sortierte*, fortlaufend nummerierte Liste (Integer als Key), während ein Dictionary eine Menge (unsortiert*) von Schlüssel-Wert-Paaren ist (beliebige Werte als Key).

    In Sachen Laufzeit ist der Zugriff auf ein Element bei beiden Datenstrukturen in der O-Notation gleich. (In der Praxis gewinnt aber das Array, wenn ein fortlaufender Index vorliegt.) Beim Einfügen und Löschen sieht es aber im Allgemeinen schon wieder anders aus.
    Von daher sollte der TE sein Problem vielleicht auch näher beschreiben. Gibt immerhin auch andere Datenformate und -strukturen: Graphen, Bäume, verkettete Listen, Heaps, etc.

    *) "sortiert" hier im Sinne von "in definierter Reihenfolge" und nicht unbedingt sortiert in Bezug auf die Elemente in der Datenstruktur
    Vielen Dank für die ganzen Antworten! Bei meiner Anwendung möchte ich in der Tat, dass Einträge nur einmal auftauchen, soweit ist Dictionary ganz praktisch. Oft möchte ich jedoch z.B. nur checken, ob ein bestimmter Key existiert und brauche den Value-Wert gar nicht -- gibt es da eine passendere Datenstruktur als ein Dictionary in Visual Basic .NET? Eine weitere Problematik ergibt sich bei mir, wenn ich einen Key aus zwei oder mehreren Werten konstruieren muss. z.B. möchte ich für eine Kombination von zwei Knoten einen bestimmten Wert herauslesen. Das Problem ist, dass ich hier keinen Array nutzen kann, da sich die Größe des Arrays immer wieder verändert und ich gehört habe, dass in diesem Fall ein Array Performance-technisch nicht sinnvoll ist. Wie kann ich das Problem bei solchen Kombinationen lösen? Das Verwenden einer Struktur (in der ich zwei Objekt-Typen reinschreibe) scheint auch langsam zu sein.
    @SZajac Dafür kannst Du eine List(Ot T) verwenden. Wenn Du ein Item einfügen willst, kannst Du mit MyList.Contains(TT) abfragen, ob es schon vorhanden ist und dann entsprechend reagieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @SZajac Möchtest Du mit einem Dictionary(Of T, T) arbeiten: ContainsKey(t)
    Falls Du mit einer List(Of T) arbeiten möchtest: Contains(t).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo,

    es gibt noch eine 3. Möglichkeit: die Keyed Collection. Sie vereint die Vorteile eines Dictionary und einer List
    https://msdn.microsoft.com/de-de/library/ms132438(v=vs.110).aspx

    Man kann auf die Collection sowohl über einen index als auch über einen Schlüssel zugreifen. Laut diverser Performancetests ist der Zugriff auf einzelne Elemente nicht wirklich langsamer als bei einem Array. Bei diesem Test hier:
    stackoverflow.com/questions/45…rmance-of-arrays-vs-lists
    ergeben sich nur marginale Unterschiede zwischen einer List und einem Array bei Zugriff per Index.
    Was laut meinen Erfahrungen und diverser Dokumentationen sehr viel Zeit kostet ist die Erweiterung einer Collection, da hier der Inhalt der Collection umkopiert wird. Abhilfe kann man hier schaffen, in dem man eine Collection gleich mit einer ausreichenden Anfangskapazität instanziert.
    Ich selbst nutze intern in Klassen, bei denen die Kapazität fix ist ausschließlich Arrays, da ich hierbei gerne die bessere Performance mitnehme bzw. hier die Vorteile einer List nicht brauche.
    Hi
    @Radinator das stimmt doch gar nicht. Dictionary kapselt an sich kein Array, der Zugriff ist ja über einen Schlüssel, somit gibt's auch kein Dictionary(Of Tier), sondern höchstens bspw. ein Dictionary(Of String, Tier).

    Dictionary wird für Zuordnungen eines Schlüssels zu einem Wert verwendet. Dabei wird intern eine effiziente Datenstruktur verwendet, die anhand der GetHashCode-Funktion arbeitet (man kann sich das so vorstellen, dass GetHashCode eine Zahl liefert, die zumindest bei zwei gleichen Objekten gleich sind. Bildet man bspw. einen Baum aus den Bits des Hashcodes, kann man effizient auf einzelne Knoten dieses Baums zugreifen, überprüfen, ob dieser vorhanden ist, usw. Ich weiß aber nicht, wie das Dictionary direkt realisiert wird, ich schätze aber, dass es so arbeitet).

    KeyedCollection ist dann zu verwenden, wenn ein Objekt seinen Schlüssel bereits selbst liefert. Ansonsten funktioniert es quasi wie ein Dictionary.

    HashSets arbeiten ähnlich, wie Dictionaries, nur gibt's keine Zuordnung von Schlüssel-Wert-Paaren. Es kann halt sehr effizient überprüft werden, ob ein bestimmter Wert in der Menge enthalten ist, usw. Bspw. gibt die Add-Funktion hier zurück, ob das Element tatsächlich eingefügt wurde; identische Elemente gibt es maximal 1 Mal in einem HashSet (ist ja bei einer (Nicht-Multi-)Menge genauso, daher auch die Bezeichnung HashSet)

    Arrays sind einfach nur eine Aneinanderreihung von n Elementen. Der Zugriff über den Index ist "maximal" effizient. Allerdings sind sie "fixed size", d.h. man kann die Größe eines Arrays nicht ändern (man beachte, dass auch Array.Resize ein neues Array erzeugt! Das Array ist bei dieser Methode als in-out bzw. ByRef markiert).

    List(Of T) hingegen hat diese Einschränkung nicht, arbeitet intern aber ebenfalls mit einem Array. Einfüge-Operationen an einem Index sind "relativ" teuer, da alle Items nach dem Index verschoben werden müssen (Kopieren von Speicher ist in der Tat nicht so schlimm, daher "relativ" teuer). Selbiges gilt für das Entfernen. Je weiter hinten das Element, desto günstiger ist das Einfügen/Entfernen/... allerdings. (LinkedList(Of T) kann nicht effizient über Indices angesteuert werden, aber Einfüge-Operationen nach oder vor einem bekannten Element sind in halt extrem günstig lösbar. Jedes Element kennt hierbei nur seine direkten Nachbarn (und die Liste, der die Knoten angehören))

    Ergo: Wenn die IDs einfach auf 0, 1, 2, 3, ... abbildbar sind, dann sind Array und List(Of T) die richtige Wahl - sofern keine Einfüge- bzw. Entfern-Operationen durchgeführt werden (dadurch müsste man ja alle nachfolgenden IDs nochmal updaten). Ansonsten wäre Dictionary zu empfehlen.

    Btw.: Der IEqualityComparer, den ein Dictionary verwendet, wenn keiner angegeben wurde, arbeitet mit Object.GetHashCode() und Object.Equals(Object) bzw. wahrscheinlich IEquatable(Of T).Equals(T).

    Viele Grüße
    ~blaze~

    ~blaze~ schrieb:


    @Radinator das stimmt doch gar nicht. Dictionary kapselt an sich kein Array,


    Natürlich kapselt eine Hashtabelle ein Array. In .NET ist es als Array von "Entry"-Structs implementiert, die jeweils den Key und den Wert enthalten. Dies ist z.B. anders als bei verketteten Listen, diese werden i.d.R. nicht mittels Arrays implementiert.
    @blaze Graphen? Ich meinte das nicht abstrakt-konzeptuell... Eine Hashtabelle benutzt intern ein Array.
    Die Hashtabelle kapselt insofern das Array, da es es für die Implementierung benötigt. Zu behaupten, die Hashtabelle wäre deshalb langsamer, das ist Schwachsinn, ja.
    List of kapselt ein Array und ist trotzdem langsamer als ein Array, wenn du Glück hast wird das meiste inlined und es ist wirklich vernachlässigbar.
    Und der Geschwindigkeitsverlust kommt von der Konzeption, denn auch wenn ein array verwendet wird ist es rein logisch kein Array.
    Ich behaupte ja auch nicht, dass es keine arrays sind,sondern nur zusammenhängender Speicher mit vorrausgehender länge.
    Außerdem nur weil .net es so macht ist es auch nicht die einzige Möglichkeit.
    Buckets sind eher ne list of array oddr var List of list bei dynamischen buckets. Oder list of linked list of array.
    Es gibt einfach zu viele Implementierungsmöglichkeiten um hier weiter sinnvoll zu diskutieren.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---