Tuesday 12 September 2017

C # Moving Average Warteschlange


Um eine asymptotische Performance von O (n) zu erreichen (wie die handcodierte Lösung tut), können Sie die Aggregate-Funktion wie in verwenden. Der akkumulierte Wert (implementiert als anonymer Typ) enthält zwei Felder: Ergebnis enthält die Ergebnisliste aufbauen bisher. Das Arbeiten enthält die letzten Perioden-1 Elemente. Die Aggregatfunktion fügt den aktuellen Wert der Arbeitsliste hinzu, baut den aktuellen Durchschnitt auf und fügt sie dem Ergebnis hinzu und entfernt dann den ersten (d. H. Ältesten) Wert aus der Arbeitsliste. Das Saatgut (d. h. der Anfangswert für die Akkumulation) wird aufgebaut, indem die ersten Perioden-1-Elemente in die Arbeits-und Initialisierungsergebnis zu einer leeren Liste gebracht werden. Folglich beginnt die Aggregation mit der Elementperiode (durch Überspringen (Perioden-1) - Elemente am Anfang). In der Funktionsprogrammierung ist dies ein typisches Verwendungsmuster für die Aggregat - (oder Falz-) Funktion, btw. Die Lösung ist nicht funktional sauber, da die gleichen Listenobjekte (Arbeit und Ergebnis) in jedem Schritt wiederverwendet werden. Im nicht sicher, wenn das Probleme verursachen könnte, wenn einige zukünftige Übersetzer versuchen, die Aggregate-Funktion automatisch zu parallellisieren (auf der anderen Seite Im auch nicht sicher, wenn das möglich ist, nachdem alle.). Eine rein funktionale Lösung sollte bei jedem Schritt neue Listen anlegen. Beachten Sie außerdem, dass C keine leistungsfähigen Listenausdrücke aufweist. In einigen hypothetischen Python-C-gemischten Pseudocode könnte man die Aggregationsfunktion schreiben, die in meiner bescheidenen Meinung ein wenig eleganter wäre :) Beachte die Laufzeit von O (n2). Da Sie bei jedem Schritt immer mehr Elemente überspringen müssen (und afaik Skip (i) I mal IEnumerator. MoveNext aufrufen muss). Siehe meine Antwort für eine Lösung in O (n) Zeit. (Ich habe gerade bemerkt, die OP-Kommentar unten, dass er sie möglicherweise erhalten die Werte aus einer SQL DB in der Zukunft.) In diesem Fall würde ich jede starke entmutigen von dieser Lösung ndash MartinStettner Mar 3 11 at 0:53 Für die effizienteste Art und Weise Möglich, einen Moving Average mit LINQ zu berechnen, sollten Sie nicht LINQ verwenden Ich schlage vor, eine Helper-Klasse, die einen gleitenden Durchschnitt auf die effizienteste Art und Weise (mit einem kreisförmigen Puffer und kausalen gleitenden durchschnittlichen Filter), dann eine Erweiterung Methode, um es zu berechnen Zugänglich für LINQ. First up, der gleitende Durchschnitt Diese Klasse bietet eine sehr schnelle und leichte Implementierung eines MovingAverage-Filters. Es erzeugt einen kreisförmigen Puffer der Länge N und berechnet eine Addition, eine Subtraktion und eine Multiplikation pro angehängten Datenpunkt im Gegensatz zu den N Multiplikations-Adds pro Punkt für die Brute-Force-Implementierung. Die oben genannten Erweiterungsmethoden wickeln die MovingAverage-Klasse und ermöglichen die Einfügung in einen IEnumerable-Stream. Um dies in einer funktionalen Weise zu tun, benötigen Sie eine Scan-Methode, die in Rx, aber nicht in LINQ. Lets schauen, wie es aussehen würde, wenn wed haben eine Scan-Methode Und heres die Scan-Methode, genommen und angepasst von hier: Dies sollte eine bessere Leistung als die Brute-Force-Methode haben, da wir eine laufende Summe verwenden, um die SMA berechnen. Um zu beginnen, müssen wir die erste Periode berechnen, die wir hier Samen nennen. Dann wird jeder nachfolgende Wert aus dem akkumulierten Samenwert berechnet. Dazu benötigen wir den alten Wert (das ist t-delta) und der neueste Wert, für den wir die Serie reihen, einmal von Anfang an und einmal durch das Delta verschoben. Am Ende führen wir einige Bereinigung durch Hinzufügen von Nullen für die Länge der ersten Periode und das Hinzufügen der anfänglichen Seed-Wert. Antwort # 1 am: Juni 19, 2008, um 22: 58An umfangreiche Prüfung von Datenstrukturen mit C 2.0 Scott Mitchell 4GuysFromRolla Update Januar 2005 Zusammenfassung: Dieser Artikel, der zweite in einer sechsteiligen Serie auf Datenstrukturen in der. NET Framework, untersucht drei der am häufigsten Studierte Datenstrukturen: die Warteschlange, den Stapel und die Hashtable. Wie auch sehen, die Warteschlange und Stapel sind spezialisierte Listen, die Speicherung für eine variable Anzahl von Objekten, aber die Beschränkung der Reihenfolge, in der die Elemente zugegriffen werden kann. Die Hashtable bietet eine Array-ähnliche Abstraktion mit größerer Indizierungsflexibilität. Wenn ein Array erfordert, dass seine Elemente durch einen Ordinalwert indiziert werden, ermöglichen Hashtables, dass Elemente von einem beliebigen Objekttyp, z. B. einem String, indiziert werden können. (19 gedruckte Seiten) Editoren Anmerkung Diese sechsteilige Artikelserie erschien ursprünglich auf MSDN Online ab November 2003. Im Januar 2005 wurde sie aktualisiert, um die neuen Datenstrukturen und Funktionen zu nutzen, die mit den. NET Framework-Versionen 2.0 und C verfügbar sind 2,0. Die ursprünglichen Artikel sind weiterhin verfügbar unter msdn. microsoft vcsharp default. aspxpull Bibliothek en-us dvvstechart html datastructuresguide. asp. Hinweis In diesem Artikel wird davon ausgegangen, dass der Leser mit C vertraut ist. Einführung In Teil 1 einer umfangreichen Untersuchung von Datenstrukturen haben wir untersucht, welche Datenstrukturen bewertet werden können, wie ihre Leistung ausgewertet werden kann und wie diese Leistungsüberlegungen die Wahl der Datenstruktur treffen Verwenden für einen bestimmten Algorithmus. Neben der Überprüfung der Grundlagen der Datenstrukturen und deren Analyse untersuchten wir auch die am häufigsten verwendete Datenstruktur, das Array. Das Array enthält einen Satz von homogenen Elementen, die durch den Ordinalwert indiziert sind. Die tatsächlichen Inhalte eines Arrays sind als zusammenhängender Block angeordnet, wodurch das Lesen oder Schreiben in ein bestimmtes Arrayelement sehr schnell erfolgt. Neben dem Standardarray bietet die. NET Framework-Basisklassenbibliothek die List-Klasse. Wie das Array ist die Liste eine Sammlung homogener Datenelemente. Mit einer Liste. Müssen Sie nicht über die Größenänderung oder Kapazitätsgrenzen Sorgen machen, und es gibt zahlreiche List-Methoden zum Suchen, Sortieren und Ändern der Liste s Daten. Wie im vorherigen Artikel beschrieben, verwendet die List-Klasse Generics, um eine typsichere, wiederverwendbare Auflistungsdatenstruktur bereitzustellen. In dieser zweiten Tranche der Artikel-Serie, weiterhin unsere Untersuchung von Array-ähnliche Datenstrukturen, indem Sie zunächst die Prüfung der Warteschlange und Stack. Diese beiden Datenstrukturen sind in einigen Aspekten der Liste ähnlich, die beide mit Hilfe von Generics implementiert werden, um eine typsichere Sammlung von Datenelementen zu enthalten. Die Warteschlange und der Stapel unterscheiden sich von der List-Klasse darin, dass es Einschränkungen gibt, wie auf die Warteschlangen - und Stapeldaten zugegriffen werden kann. Nach unserem Blick auf die Warteschlange und Stack, gut verbringen den Rest dieses Artikels graben in die Hashtable-Datenstruktur. Ein Hashtable. Die manchmal als assoziatives Array bezeichnet wird, speichert eine Sammlung von Elementen, indiziert diese Elemente jedoch durch ein beliebiges Objekt (wie einen String), im Gegensatz zu einem Ordinalindex. Bereitstellung von First Come, First Served Job Processing Wenn Sie die Schaffung jeder Art von Computer-Service ist, ist ein Computer-Programm, das mehrere Anfragen aus mehreren Quellen für einige Aufgaben abgeschlossen werden kann, ist ein Teil der Herausforderung der Erstellung des Dienstes ist die Entscheidung, in welcher Reihenfolge Werden die eingehenden Anforderungen behandelt. Die beiden häufigsten Ansätze verwendet werden: First come, zuerst serviert Priority-basierte Verarbeitung First come, zuerst serviert wird die Job-Scheduling-Aufgabe youll finden Sie bei Ihrem Lebensmittelgeschäft, der Bank und Lizenzierung Abteilungen. Die warten auf Service stehen in einer Linie. Die Leute vor Ihnen werden vor Ihnen serviert werden, während die Menschen hinter Ihnen danach serviert werden. Prioritätsbasierte Prozesse dienen denen mit einer höheren Priorität vor denen mit geringerer Priorität. Zum Beispiel, eine Krankenhaus-Notaufnahme verwendet diese Strategie, die Entscheidung, jemanden mit einer potenziell tödlichen Wunde vor jemand mit einer weniger bedrohlichen Wunde, unabhängig davon, wer zuerst angekommen zu helfen. Stellen Sie sich vor, dass Sie einen Computerdienst erstellen müssen und dass Sie Anfragen in der Reihenfolge behandeln möchten, in der sie empfangen wurden. Da die Anzahl der eingehenden Anforderungen schneller geschehen kann, als Sie sie verarbeiten können, müssen Sie die Anforderungen in einer Art von Puffer platzieren, die die Reihenfolge, in der sie eintreffen, bewahren können. Eine Möglichkeit besteht darin, eine Liste und eine Integer-Variable NextJobPos zu verwenden, um die Position des nächsten zu bearbeitenden Jobs anzugeben. Wenn jede neue Jobanfrage eingeht, verwenden Sie einfach die Lists Add () - Methode, um sie am Ende der Liste hinzuzufügen. Wann immer Sie bereit sind, einen Job im Puffer zu verarbeiten, packen Sie den Job an der nextJobPos Position in der Liste und inkrementieren nextJobPos. Das folgende einfache Programm veranschaulicht diesen Algorithmus: Die Ausgabe dieses Programms ist wie folgt: Während dieser Ansatz ist ziemlich einfach und unkompliziert, es ist schrecklich ineffizient. Für den Anfang wird die Liste weiterhin mit jedem Job, der dem Puffer hinzugefügt wird, ungebrochen, auch wenn die Aufträge unmittelbar nach dem Hinzufügen zum Puffer verarbeitet werden. Betrachten Sie den Fall, dass jede Sekunde ein neuer Job zum Puffer hinzugefügt wird und ein Job aus dem Puffer entfernt wird. Dies bedeutet, dass einmal die Sekunde die AddJob () - Methode aufgerufen wird, die die Methode Lists Add () aufruft. Wenn die Add () - Methode fortwährend aufgerufen wird, wird die interne Array-Größe der Listen bei Bedarf kontinuierlich verdoppelt. Nach fünf Minuten (300 Sekunden) wird das interne Array der Liste für 512 Elemente ausgelegt, obwohl nie zuvor mehr als ein Auftrag im Puffer vorhanden war. Dieser Trend wird natürlich so lange fortgesetzt, wie das Programm weiterläuft und die Jobs weitergehen. Der Grund, weshalb die Liste in so lächerlichen Proportionen wächst, ist, dass die Pufferplätze, die für alte Jobs verwendet werden, nicht zurückgefordert werden. Das heißt, wenn der erste Job dem Puffer hinzugefügt und dann verarbeitet wird, ist klar, dass der erste Punkt in der Liste bereit ist, wiederverwendet zu werden. Betrachten Sie den Job-Zeitplan in der vorherigen Codebeispiel präsentiert. Nach den ersten beiden Zeilen AddJob (1) und AddJob (2) sieht die Liste wie in Abbildung 1 aus. Abbildung 1. Die ArrayList nach den ersten beiden Zeilen des Codes Beachten Sie, dass es in der Liste an dieser Stelle 16 Elemente gibt, da die Liste war Initialisiert mit einer Kapazität von 16 in dem obigen Code. Als nächstes wird die GetNextJob () - Methode aufgerufen, die den ersten Job löscht, was zu Fiure 2 führt. Abbildung 2. Programm, nachdem die GetNextJob () - Methode aufgerufen wird Wenn AddJob (3) ausgeführt wird, müssen wir dem Puffer einen anderen Job hinzufügen. Deutlich ist das erste Listenelement (Index 0) zur Wiederverwendung verfügbar. Anfänglich könnte es sinnvoll sein, den dritten Job in den 0-Index zu setzen. Allerdings kann dieser Ansatz beseitigt werden, indem man bedenkt, was passieren würde, wenn nach AddJob (3) AddJob (4). Gefolgt von zwei Aufrufen zu GetNextJob (). Wenn wir den dritten Job in den 0-Index und dann den vierten Job in den 2-Index platziert haben, haben wir so etwas wie das in Abbildung 3 dargestellte Problem. Abbildung 3.Erstellung, die durch Platzieren von Jobs im O-Index erzeugt wurde Nun, wenn GetNextJob () war Würde der zweite Job aus dem Puffer entfernt und nextJobPos würde inkrementiert werden, um auf Index 2 zu zeigen. Daher wurde beim erneuten Aufruf von GetNextJob () der vierte Job entfernt und vor dem dritten Job verarbeitet Zuerst kommen, zuerst gedienter Auftrag, den wir beibehalten müssen. Das Problem dieses Problems besteht darin, dass die Liste die Liste der Jobs in einer linearen Reihenfolge darstellt. Das heißt, wir müssen die neuen Arbeitsplätze auf der rechten Seite der alten Arbeitsplätze zu halten, um sicherzustellen, dass die richtige Reihenfolge der Verarbeitung beibehalten wird. Wann immer wir das Ende der Liste treffen, wird die Liste verdoppelt, auch wenn es ungenutzte Listenelemente aufgrund von Aufrufen von GetNextJob () gibt. Um dieses Problem zu beheben, müssen wir unsere Liste Rundschreiben. Ein kreisförmiges Array ist eines, das keinen definitiven Anfang oder kein bestimmtes Ende hat. Vielmehr müssen wir Variablen verwenden, um die Anfangs - und Endpositionen des Arrays zu erinnern. Eine grafische Darstellung eines kreisförmigen Arrays ist in Abbildung 4 dargestellt. Abbildung 4. Beispiel eines kreisförmigen Arrays Mit einem kreisförmigen Array fügt die AddJob () - Methode den neuen Job in Index endPos hinzu und erhöht dann endPos. Die GetNextJob () - Methode rupft den Job aus startPos. Setzt das Element am startPos-Index auf null. Und inkrementiert startPos. Ich habe die Wort-Inkremente in Anführungszeichen, weil hier Inkrementierung ist eine Kleinigkeit komplexer als nur das Hinzufügen eines der Variablen aktuellen Wert. Um zu sehen, warum wir nicht nur 1 hinzufügen können, betrachten wir den Fall, wenn endPos 15 entspricht. Wenn wir endPos durch Hinzufügen von 1 erhöhen, wird endPos gleich 16. Im nächsten AddJob () - Aufruf versucht der Index 16, darauf zuzugreifen In einer IndexOutOfRangeException. Wenn endPos gleich 15 ist, wollen wir endPos inkrementieren, indem Sie es auf 0 zurücksetzen. Dies kann entweder durch Erstellen einer Inkrement (Variablen) - Funktion erfolgen, die überprüft, ob die übergebene Variable der Arraysgröße entspricht, und wenn ja, Setzen Sie es auf 0 zurück. Alternativ kann die Variable ihren Wert um 1 erhöht haben und dann durch die Größe des Arrays mod_modiert werden. In einem solchen Fall würde der Code für increment () folgendermaßen aussehen: Hinweis: Der Modulo-Operator,. Wenn verwendet wie x y. Berechnet den Rest von x dividiert durch y. Der Rest bleibt immer zwischen 0 und y 1. Dieser Ansatz funktioniert gut, wenn unser Puffer nie mehr als 16 Elemente haben wird, aber was passiert, wenn wir einen neuen Job zum Puffer hinzufügen wollen, wenn es bereits 16 Jobs vorhanden sind Wie bei den Listen Add () - Methode, müssen auch die Größe des kreisförmigen Arrays entsprechend, z. B., verdoppeln die Größe des Arrays. Die System. Collections. Generic. Queue-Klasse Die Funktionalität, die wir soeben beschrieben haben, um Elemente in einen Puffer zu kopieren und zu entfernen, ist die Warteschlange in einer Standarddatenstruktur. Die. NET Framework-Basisklassenbibliothek stellt die Klasse System. Collections. Generic. Queue bereit, die Generics verwendet, um eine typsichere Warteschlangenimplementierung bereitzustellen. Während unser früherer Code die Methoden AddJob () und GetNextJob () bereitstellte, bietet die Warteschlangenklasse identische Funktionalität mit ihren Methoden Enqueue (item) bzw. Dequeue (). Hinter den Kulissen behält die Warteschlangenklasse ein internes kreisförmiges Array und zwei Variablen, die als Marker für den Anfang und das Ende des kreisförmigen Arrays dienen: Kopf und Schwanz. Die Methode Enqueue () startet mit der Bestimmung, ob genügend Kapazität zum Hinzufügen des neuen Elements zur Warteschlange vorhanden ist. Wenn dies der Fall ist, fügt er lediglich das Element zum kreisförmigen Array am Schwanzindex hinzu und hebt dann den Schwanz unter Verwendung des Modulusoperators auf, um sicherzustellen, daß der Schwanz die interne Arrayslänge nicht überschreitet. Wenn jedoch nicht genügend Platz vorhanden ist, wird das Array um einen bestimmten Wachstumsfaktor erhöht. Dieser Wachstumsfaktor hat einen Standardwert von 2.0, wodurch die interne Arraygröße verdoppelt wird, aber Sie können diesen Faktor optional im Konstruktor der Warteschlangenklasse angeben. Die Dequeue () - Methode gibt das aktuelle Element aus dem Kopfindex zurück. Es setzt auch das Kopfindexelement auf Null und erhöht den Kopf. Für jene Zeiten, in denen Sie das head-Element ansehen, aber nicht tatsächlich dequeue können, bietet die Queue-Klasse auch eine Peek () - Methode. Was zu beachten ist, ist, dass die Warteschlange im Gegensatz zur Liste keinen zufälligen Zugriff erlaubt. Das heißt, Sie können nicht auf das dritte Element in der Warteschlange ohne Dequeing die ersten beiden Elemente. Die Queue-Klasse verfügt jedoch über eine Contains () - Methode, so können Sie bestimmen, ob ein bestimmtes Element in der Warteschlange vorhanden ist oder nicht. Es gibt auch eine ToArray () - Methode, die ein Array mit den Queue-Elementen zurückgibt. Wenn Sie wissen, dass Sie wahlfreien Zugriff benötigen, ist die Warteschlange nicht die Datenstruktur, die usethe Liste ist. Die Warteschlange ist jedoch ideal für Situationen, in denen Sie nur daran interessiert sind, Elemente in der genauen Reihenfolge zu verarbeiten, mit der sie empfangen wurden. Hinweis Sie können Warteschlangen hören, die als FIFO-Datenstrukturen bezeichnet werden. FIFO steht für First In, First Out und ist gleichbedeutend mit der Bearbeitungsreihenfolge von first come, first served. Schauen Sie sich die Stack-Datenstruktur an: First Come, Last Served Die Warteschlange-Datenstruktur bietet erstmals einen ersten Zugriff, indem Sie intern ein kreisförmiges Array vom Typ object verwenden. Die Warteschlange bietet einen solchen Zugriff, indem Sie eine Enqueue () - und Dequque () - Methode aussetzen. Zuerst kommen, erste dienen Verarbeitung hat eine Reihe von realen Anwendungen, vor allem in Service-Programme wie Web-Servern, Druck-Warteschlangen und andere Programme, die mehrere eingehende Anfragen behandeln. Ein anderes allgemeines Verarbeitungsschema in Computerprogrammen wird zuerst gekommen, zuletzt gedient. Die Datenstruktur, die diese Form des Zugriffs bereitstellt, wird als Stack bezeichnet. Die. NET Framework Base-Klassenbibliothek enthält eine Stack-Klasse im Sytem. Collections. Generic-Namespace. Wie die Queue-Klasse behält die Stack-Klasse ihre Elemente intern mit einem kreisförmigen Array. Die Stack-Klasse macht ihre Daten über zwei Methoden verfügbar: Push (Element). Das das übergebene Element zum Stapel hinzufügt, und Pop (). Die das Element an der Spitze des Stapels entfernt und zurückgibt. Ein Stapel kann grafisch als vertikale Sammlung von Elementen visualisiert werden. Wenn ein Gegenstand auf den Stapel gedrückt wird, wird er auf alle anderen Gegenstände gelegt. Beim Öffnen eines Elements wird das Element von der Oberseite des Stapels entfernt. Die folgenden zwei Figuren stellen graphisch einen Stapel dar, nachdem die Punkte 1, 2 und 3 in dieser Reihenfolge auf den Stapel gedrückt worden sind, und dann nach einem Pop. Abbildung 5. Grafische Darstellung eines Stapels mit drei Elementen Abbildung 6. Grafische Darstellung eines Stapels mit drei Elementen nach einem Popup Wenn die interne Stapel-Array-Größe angepasst werden muss, wird sie automatisch um das Doppelte der Anfangsgröße erhöht. (Erinnern Sie sich, dass mit der Warteschlange dieser Wachstumsfaktor optional über den Konstruktor festgelegt werden kann.) Hinweis Stacks werden oft als LIFO-Datenstrukturen oder Last In, First Out bezeichnet. Stacks: Eine gemeinsame Metapher in der Informatik Wenn man über Warteschlangen reden, ist es leicht, viele real-world Parallelen wie Linien in der Bäckerei, Drucker Job Verarbeitung und so weiter zu zaubern. Aber real-world Beispiele von Stacks in Aktion sind schwerer zu kommen. Trotz dieser, Stacks sind eine herausragende Datenstruktur in einer Vielzahl von Computer-Anwendungen. Betrachten Sie z. B. eine beliebige imperative Programmiersprache wie C. Wenn ein C-Programm ausgeführt wird, hält die CLR einen Anrufstapel, der unter anderem die Funktionsaufrufe verfolgt. Jedes Mal, wenn eine Funktion aufgerufen wird, wird ihre Information dem Aufruf-Stack hinzugefügt. Nach Abschluss der Funktionen werden die zugehörigen Informationen aus dem Stapel gelöscht. Die Informationen am Anfang des Aufruf-Stacks repräsentieren die laufende Funktion. (Für eine visuelle Demonstration der Funktion Aufruf Stack erstellen Sie ein Projekt in Visual Studio. NET, legen Sie einen Haltepunkt und gehen Sie zu Debug Start. Wenn der Breakpoint hits, zeigen Sie das Fenster Call Stack aus Debug Windows Call Stack.) Stacks sind auch häufig (Von einfachen algebraischen Aussagen zu Computerprogrammiersprachen) als Mittel zur Simulation von Rekursionen und sogar als Befehlsausführungsmodell verwendet werden. Die Einschränkungen der Ordinal Indexing Recall aus Teil 1 dieser Artikel-Serie, dass das Markenzeichen des Arrays ist, dass es eine homogene Sammlung von Elementen indiziert mit einem Ordinalwert bietet. Das heißt, auf das i-te Element eines Arrays kann in konstanter Zeit zum Lesen oder Schreiben zugegriffen werden. (Man erinnere sich, daß konstante Zeit als O (1) bezeichnet wurde.) Selten kennen wir die Ordnungsposition der Daten, an denen wir interessiert sind. Betrachten Sie zum Beispiel eine Mitarbeiterdatenbank. Die Mitarbeiter können durch ihre Sozialversicherungsnummer eindeutig identifiziert werden, die die Form DDD-DD-DDDD hat, wobei D eine Ziffer (0-9) ist. Wenn wir ein Array aller Mitarbeiter hatten, die nach dem Zufallsprinzip angeordnet waren, würde die Suche nach dem Mitarbeiter 111-22-3333 potenziell nach allen Elementen des Mitarbeiter-Arrays suchen, eine O (n) Operation. Ein etwas besserer Ansatz wäre, die Mitarbeiter nach ihren Sozialversicherungsnummern zu sortieren, was die asymptotische Suchzeit bis auf O (log n) reduzieren würde. Im Idealfall, wed gerne in der Lage zu tun ist Zugriff auf eine Mitarbeiter Datensätze in O (1) Zeit. Eine Möglichkeit, dies zu erreichen würde ein riesiges Array mit einem Eintrag für jeden möglichen Sozialversicherungsnummer Wert zu bauen. Das heißt, unser Array würde am Element 000-00-0000 beginnen und gehen Sie zu Element 999-99-9999, wie in Abbildung 7 gezeigt. Abbildung 7. Array zeigt alle möglichen Elemente für eine 9-stellige Zahl Wie diese Abbildung zeigt, jede Mitarbeiter-Datensatz enthält Informationen wie Name, Telefon, Gehalt, und so weiter, und wird von den Mitarbeitern Sozialversicherungsnummer indiziert. Mit einem solchen Schema können alle Mitarbeiter Informationen in konstanter Zeit zugegriffen werden. Der Nachteil dieses Ansatzes ist seine extreme Verschwendung: Es gibt insgesamt 10 9 das ist eine Milliarde (1.000.000.000) verschiedene Sozialversicherungsnummern. Für ein Unternehmen mit 1.000 Mitarbeitern würden nur 0,0001 dieses Arrays genutzt. (Um die Dinge in die Perspektive zu bringen, müsste Ihr Unternehmen etwa ein Sechstel der Weltbevölkerung beschäftigen, um dieses Array in der Nähe voll ausgelastet zu machen.) Komprimieren Ordinal Indexing mit einer Hash-Funktion Erstellen eines 1 Milliarde-Element-Array, um Informationen über 1.000 zu speichern Mitarbeiter ist eindeutig inakzeptabel. Die Leistung, in der Lage ist, auf eine Mitarbeiterinformation in konstanter Zeit zuzugreifen, ist jedoch höchst wünschenswert. Eine Option wäre, die Sozialversicherungsnummer span zu reduzieren, indem nur die letzten vier Stellen der Sozialversicherungsnummer eines Arbeitnehmers verwendet werden. Das heißt, anstelle eines Arrays, das sich von 000-00-0000 bis 999-99-9999 erstreckt, würde das Array nur von 0000 bis 9999 spannen. Abbildung 8 unten zeigt eine grafische Darstellung dieses abgebauten Arrays. Abbildung 8. Trimm-down-Array Dieser Ansatz bietet sowohl die Kosten für die konstante Nachschlagezeit als auch eine wesentlich bessere Platzausnutzung. Die Wahl der letzten vier Ziffern der Sozialversicherungsnummer war eine willkürliche Wahl. Wir hätten die mittleren vier Ziffern oder die ersten, dritten, achten und neunten benutzen können. Die mathematische Umwandlung der neunstelligen Sozialversicherungsnummer zu einer vierstelligen Zahl heißt Hashing. Ein Array, das Hashing verwendet, um seinen Indexerraum zu komprimieren, wird als Hash-Tabelle bezeichnet. Eine Hashfunktion ist eine Funktion, die dieses Hashing ausführt. Für die Sozialversicherungsnummer Beispiel, unsere Hash-Funktion, H. Kann folgendermaßen beschrieben werden: Die Eingaben zu H können jede neunstellige Sozialversicherungsnummer sein, während das Ergebnis von H eine vierstellige Zahl ist, die lediglich die letzten vier Ziffern der neunstelligen Sozialversicherungsnummer ist. Mathematisch formuliert H Elemente aus der Menge der neunstelligen Sozialversicherungsnummern zu Elementen aus der Menge der vierstelligen Sozialversicherungsnummern, wie in Abbildung 9 graphisch dargestellt. Abbildung 9. Graphische Darstellung einer Hashfunktion Die obige Abbildung Veranschaulicht ein Verhalten von Hash-Funktionen genannt Kollisionen. Im Allgemeinen können Sie mit Hashfunktionen zwei Elemente im größeren Satz finden, die auf denselben Wert im kleineren Satz zuordnen. Mit unserer Sozialversicherungsnummer hashing Funktion, werden alle Sozialversicherungsnummern, die in 0000 enden, auf 0000 abbilden. Das heißt, der Hashwert für 000-00-0000, 113-14-0000, 933-66-0000 und viele andere werden alle sein 0000. (In der Tat gibt es genau 10.5 oder 100.000, Sozialversicherungsnummern, die in 0000 enden.) Um es wieder in den Kontext unserer früheren Beispiel, betrachten, was passieren würde, wenn ein neuer Mitarbeiter mit sozialer Sicherheit hinzugefügt wurde Nummer 123-00-0191. Der Versuch, diesen Mitarbeiter zum Array hinzuzufügen, würde ein Problem verursachen, da bereits ein Mitarbeiter am Array-Speicherort 0191 (Jisun Lee) existiert. Mathematische Anmerkung Eine Hashfunktion kann in mathematisch präziseren Ausdrücken als Funktion f beschrieben werden. A - gt B. Weil A gt B der Fall sein muss, dass f nicht eins zu eins ist, wird es zu Kollisionen kommen. Offensichtlich kann das Auftreten von Kollisionen Probleme verursachen. Im nächsten Abschnitt, betrachten Sie die Korrelation zwischen der Hash-Funktion und das Auftreten von Kollisionen und dann kurz untersuchen einige Strategien für den Umgang mit Kollisionen. In dem Abschnitt danach, wenden Sie unsere Aufmerksamkeit auf die System. Collections. Hashtable-Klasse, die eine Implementierung einer Hash-Tabelle zur Verfügung stellt. Nun schauen Sie sich die Hashtable-Klasse Hash-Funktion, Collision Resolution-Strategie, und einige Beispiele für die Verwendung der Hashtable-Klasse in der Praxis. Nach einem Blick auf die Hashtable-Klasse studieren Sie auch die Dictionary-Klasse, die der. NET Framework 2.0 Base-Klassenbibliothek hinzugefügt wurde. Die Dictionary-Klasse ist identisch mit der Hashtable, außer für zwei Unterschiede: Es verwendet Generics und ist daher stark typisiert. Es verwendet eine alternative Kollisionsauflösung Strategie. Kollisionsvermeidung und Auflösung Beim Hinzufügen von Daten zu einer Hash-Tabelle wirft eine Kollision einen Affenschlüssel in den gesamten Vorgang. Ohne eine Kollision können wir das eingefügte Element mit einer Kollision in die Hash-Position einfügen, aber wir müssen über eine korrektive Vorgehensweise entscheiden. Aufgrund der erhöhten Kosten bei Kollisionen sollte unser Ziel sein, möglichst wenig Kollisionen zu haben. Die Häufigkeit der Kollisionen ist direkt mit der verwendeten Hashfunktion und der Verteilung der in die Hashfunktion übergebenen Daten korreliert. In unserer Sozialversicherungsnummer Beispiel, mit den letzten vier Stellen eines Arbeitnehmers Sozialversicherungsnummer ist eine ideale Hash-Funktion unter der Annahme, dass Sozialversicherungsnummern zufällig zugeordnet sind. Wenn jedoch die Sozialversicherungsnummern so vergeben werden, dass diejenigen, die in einem bestimmten Jahr oder Standort geboren wurden, eher die gleichen letzten vier Ziffern haben, könnte die Verwendung der letzten vier Ziffern eine große Anzahl von Kollisionen verursachen, wenn Ihre Angestelltengeburtsdaten und Geburtsorte liegen Sind nicht gleichmäßig verteilt. Hinweis: Eine gründliche Analyse einer Hash-Funktion Wert erfordert ein wenig Erfahrung mit Statistiken, die über den Rahmen dieses Artikels. Im Wesentlichen wollen wir sicherstellen, dass für eine Hash-Tabelle mit k Schlitzen die Wahrscheinlichkeit, dass ein zufälliger Wert aus der Hash-Funktionsdomäne auf ein bestimmtes Element im Bereich liegt, 1 k beträgt. Die Auswahl einer geeigneten Hashfunktion wird als Kollisionsvermeidung bezeichnet. In diesem Bereich ist viel studiert worden, da die verwendete Hash-Funktion die Gesamtleistung der Hash-Tabelle stark beeinflussen kann. In den folgenden Abschnitten sehen Sie auch die Hash-Funktion aus, die von den Hashtable - und Dictionary-Klassen in. NET Framework verwendet wird. Im Falle einer Kollision gibt es eine Reihe von Strategien, die eingesetzt werden können. Die Aufgabe, Kollision Auflösung. Ist, einen anderen Platz zu finden, um das Objekt, das in die Hash-Tabelle eingefügt wird, zu setzen, da die tatsächliche Position bereits genommen wurde. Einer der einfachsten Ansätze heißt lineare Sondierung und arbeitet wie folgt: Wenn ein neues Element in die Hash-Tabelle eingefügt wird, verwenden Sie die Hashfunktion, um festzustellen, wo in der Tabelle sie gehört. Überprüfen Sie, ob ein Element bereits an der Stelle in der Tabelle vorhanden ist. Wenn der Punkt leer ist, platzieren Sie das Element und kehren Sie zurück, andernfalls gehen Sie zu Schritt 3. Wenn die Position, auf die die Hashfunktion verweist, auf die Position i war. Einfach den Standort i 1 nachsehen, ob dieser verfügbar ist. Wenn es auch genommen wird, überprüfen Sie i 2, und so weiter, bis ein offener Punkt gefunden wird. Betrachten Sie den Fall, in dem die folgenden vier Mitarbeiter in die Hash-Tabelle eingefügt wurden: Alice (333-33-1234), Bob (444-44-1234), Cal (555-55-1237), Danny (000-00-1235) , Und Edward (111-00-1235). Nach diesen Einsätzen wird die Hash-Tabelle aussehen: Abbildung 10. Hash-Tabelle von vier Mitarbeitern mit ähnlichen Zahlen Alices Sozialversicherungsnummer ist zu 1234 gehastet, und sie wird an Ort 1234 eingefügt. Als nächstes wird Bobs Sozialversicherungsnummer auf 1234 gehasht, aber Alice ist bereits am Punkt 1234, also nimmt Bob die nächste verfügbare Stelle, die 1235 ist. Nachdem Bob Cal eingefügt ist, hat sein Wert hashing zu 1237. Weil niemand derzeit 1237 besetzt ist, wird Cal dort eingesetzt. Danny ist der nächste, und seine Sozialversicherungsnummer ist zu 1235 gehastet. 1235 wird genommen, 1236 wird überprüft, und weil 1236 offen ist, wird Danny dort platziert. Schließlich wird Edward eingefügt, seine Sozialversicherungsnummer auch hashing zu 1235. 1235 wird genommen, also wird 1236 überprüft. Das ist auch genommen, so 1237 wird überprüft. Das ist besetzt von Cal, so 1238 wird überprüft, die offen ist, also wird Edward dort platziert. Zusätzlich zum Verkleben des Einfügungsprozesses stellen Kollisionen auch ein Problem beim Suchen einer Hash-Tabelle dar. Zum Beispiel, angesichts der Hash-Tabelle in Abbildung 10, stellen Sie sich vor, wir wollten auf Informationen über Edward zugreifen. Daher nehmen wir Edwards Sozialversicherungsnummer, 111-00-1235, hash es bis 1235, und starten unsere Suche dort. Jedoch, an Punkt 1235 finden wir Bob, nicht Edward. Also haben wir 1236 zu überprüfen, aber Dannys gibt. Unsere lineare Suche fährt fort, bis wir entweder Edward finden oder einen leeren Schlitz treffen. Wenn wir einen leeren Punkt erreichen, wissen wir, dass Edward nicht in unserer Hashtabelle ist. Lineares Sondieren, während einfach, ist nicht eine sehr gute Kollisionslösungsstrategie, weil es zu Clustering führt. Das heißt, stellen Sie sich vor, dass die ersten 10 Mitarbeiter, die wir alle haben, die soziale Sicherheit Hash auf den gleichen Wert, sagen 3344. Dann 10 aufeinander folgenden Spots werden von 3344 bis 3353 genommen werden. Dieser Cluster erfordert lineare Prüfung jederzeit von einem dieser 10 Mitarbeiter zugegriffen wird. Darüber hinaus werden alle Mitarbeiter mit Hash-Werte von 3345 bis 3353 zu dieser Clustergröße hinzuzufügen. Für schnelle Lookups, wollen wir die Daten in der Hash-Tabelle gleichmäßig verteilt, nicht gruppiert um einige Punkte. Eine umfassendere Sondierungstechnik ist quadratisches Sondieren. Die die quadratische Entfernung von Flecken beginnt. Das heißt, wenn der Schlitz s genommen wird, anstatt den Schlitz s & sub1 ;, dann s & sub2 ;, und so weiter, wie bei der linearen Abtastung, die quadratische Abtastung den Schlitz s & sub1; & sub2; zuerst, dann s & sub1; 2. dann s 3 2 und so weiter. Allerdings kann auch quadratisches Hashing zu Clusterbildung führen. Im nächsten Abschnitt, betrachten Sie auch eine dritte Kollisions-Auflösung Technik genannt Wiederherstellung. Die von der. NET Frameworks Hashtable-Klasse verwendet wird. Im letzten Abschnitt, schauen Sie sich die Dictionary-Klasse, die eine Kollision Auflösung Technik kennt als Verkettung verwendet. Die System. Collections. Hashtable-Klasse Die. NET Framework-Basisklassenbibliothek enthält eine Implementierung einer Hash-Tabelle in der Hashtable-Klasse. Beim Hinzufügen eines Elements zu der Hashtable müssen Sie nicht nur das Element angeben, sondern auch den eindeutigen Schlüssel, auf den das Element zugegriffen wird. Sowohl der Schlüssel als auch das Element können von jedem Typ sein. In unserem Mitarbeiter Beispiel wäre der Schlüssel die Mitarbeiter Sozialversicherungsnummer. Elemente werden der Hashtable mithilfe der Add () - Methode hinzugefügt. Um ein Element aus der Hashtable abzurufen, können Sie die Hashtable durch den Schlüssel indizieren, so wie Sie ein Array durch einen Ordinalwert indexieren würden. Das folgende kurze Programm C demonstriert dieses Konzept. Es fügt eine Anzahl von Elementen zu einer Hashtable hinzu und assoziiert einen String-Schlüssel mit jedem Element. Dann kann auf das jeweilige Element mit seinem String-Schlüssel zugegriffen werden. Dieser Code zeigt auch die ContainsKey () - Methode, die einen Booleschen zurückgibt, der angibt, ob ein bestimmter Schlüssel in der Hashtable gefunden wurde oder nicht. Die Hashtable-Klasse enthält eine Keys-Eigenschaft, die eine Auflistung der in Hashtable verwendeten Schlüssel zurückgibt. Diese Eigenschaft kann verwendet werden, um die Elemente in einer Hashtable aufzuzählen, wie unten gezeigt: Stellen Sie fest, dass die Reihenfolge, mit der die Elemente eingefügt werden, und die Reihenfolge der Schlüssel in der Keys-Sammlung nicht unbedingt identisch sind. Die Reihenfolge der Keys-Sammlung basiert auf dem Schlitz, auf dem die Tastenposition gespeichert wurde. Der Schlitz, den ein Gegenstand speichert, hängt von dem Schlüsselhashwert und der Kollisionslösungsstrategie ab. Wenn Sie den obigen Code ausführen, können Sie sehen, dass die Reihenfolge, in der die Elemente aufgelistet werden, nicht unbedingt mit der Reihenfolge übereinstimmt, mit der die Elemente der Hashtable hinzugefügt wurden. Ausführen der obigen Codeausgaben: Obwohl die Daten in die Hashtable in der Reihenfolge Scott, Sam, Jisun eingefügt wurden. The Hashtable Classs Hash Function The hash function of the Hashtable class is a bit more complex than the social security number hash code we examined earlier. First, keep in mind that the hash function must return an ordinal value. This was easy to do with the social security number example since the social security number is already a number itself. To get an appropriate hash value, we merely chopped off all but the final four digits. But realize that the Hashtable class can accept a key of any type. As we saw in a previous example, the key could be a string, like Scott or Sam. In such a case, it is only natural to wonder how a hash function can turn a string into a number. This magical transformation can occur thanks to the GetHashCode(). which is defined in the System. Object class. The Object classs default implementation of GetHashCode() returns a unique integer that is guaranteed not to change during the lifetime of the object. Because every type is derived, either directly or indirectly, from Object. all objects have access to this method. Therefore, a string, or any other type, can be represented as a unique number. Of course, this method can be overridden to provide a hash function more suitable to a specific class. (The Point class in the System. Drawing namespace, for example, overrides GetHashCode(). returning the XOR of its x and y member variables.) The Hashtable classs hash function is defined as follows: Here, GetHash( key ) is, by default, the result returned by key s call to GetHashCode() (although when using the Hashtable you can specify a custom GetHash() function). GetHash( key ) gtgt 5 computes the hash for key and then shifts the result 5 bits to the right this has the effect of dividing the hash result by 32. As discussed earlier in this article, the operator performs modular arithmetic. hashsize is the number of total slots in the hash table. (Recall that x y returns the remainder of x y . and that this result is always between 0 and y 1.) Due to these mod operations, the end result is that H ( key ) will be a value between 0 and hashsize 1. Since hashsize is the total number of slots in the hash table, the resulting hash will always point to within the acceptable range of values. Collision Resolution in the Hashtable Class Recall that when inserting an item into or retrieving an item from a hash table, a collision can occur. When inserting an item, an open slot must be found. When retrieving an item, the actual item must be found if it is not in the expected location. Earlier we briefly examined two collusion resolution strategies: Linear probing Quardratic probing The Hashtable class uses a different technique referred to as rehasing . (Some sources refer to rehashing as double hashing .) Rehasing works as follows: there is a set of hash different functions, H 1. H n. and when inserting or retrieving an item from the hash table, initially the H 1 hash function is used. If this leads to a collision, H 2 is tried instead, and onwards up to H n if needed. The previous section showed only one hash function, which is the initial hash function ( H 1 ). The other hash functions are very similar to this function, only differentiating by a multiplicative factor. In general, the hash function H k is defined as: Mathematical Note With rehasing it is important that each slot in the hash table is visited exactly once when hashsize number of probes are made. That is, for a given key you dont want H i and H j to hash to the same slot in the hash table. With the rehashing formula used by the Hashtable class, this property is maintained if the result of (1 (((GetHash(key) gtgt 5) 1) (hashsize 1)) and hashsize are relatively prime. (Two numbers are relatively prime if they share no common factors.) These two numbers are guaranteed to be relatively prime if hashsize is a prime number. Rehasing provides better collision avoidance than either linear or quadratic probing. Load Factors and Expanding the Hashtable The Hashtable class contains a private member variable called loadFactor that specifies the maximum ratio of items in the Hashtable to the total number of slots. A loadFactor of, say, 0.5, indicates that at most the Hashtable can only have half of its slots filled with items and the other half must remain empty. In an overloaded form of the Hashtables constructor, you can specify a loadFactor value between 0.1 and 1.0. Realize, however, that whatever value you provide, it is scaled down 72, so even if you pass in a value of 1.0 the Hashtable classs actual loadFactor will be 0.72. The 0.72 was found by Microsoft to be the optimal load factor, so consider using the default 1.0 load factor value (which gets scaled automatically to 0.72). Therefore, you would be encouraged to use the default of 1.0 (which is really 0.72). Note I spent a few days asking various listservs and folks at Microsoft why this automatic scaling was applied. I wondered why, if they wanted to values to be between 0.072 and 0.72, why not make that the legal range I ended up talking to the Microsoft team that worked on the Hashtable class and they shared their reason for this decision. Specifically, the team found through empirical testing that values greater than 0.72 seriously degraded the performance. They decided that the developer using the Hashtable would be better off if they didnt have to remember a seeming arbitrary value in 0.72, but instead just had to remember that a value of 1.0 gave the best results. So this decision, essentially, sacrifices functionality a bit, but makes the data structure easier to use and will cause fewer headaches in the developer community. Whenever a new item is added to the Hashtable class, a check occurs to make sure adding the new item wont push the ratio of items to slots past the specified maximum ratio. If it will, then the Hashtable is expanded . Expansion occurs in two steps: The number of slots in the Hashtable is approximately doubled. More precisely, the number of slots is increased from the current prime number value to the next largest prime number value in an internal table. Recall that for rehashing to work properly, the number of hash table slots needs to be a prime number. Because the hash value of each item in the hash table is dependent on the number of total slots in the hash table, all of the values in the hash table need to be rehashed (because the number of slots increased in step 1). Fortunately the Hashtable class hides all this complexity in the Add() method, so you dont need to be concerned with the details. The load factor influences the overall size of the hash table and the expected number of probes needed on a collision. A high load factor, which allows for a relatively dense hash table, requires less space but more probes on collisions than a sparsely dense hash table. Without getting into the rigors of the analysis, the expected number of probes needed when a collision occurs is at most 1 (1 lf ), where lf is the load factor. As aforementioned, Microsoft has tuned the Hashtable to use a default load factor of 0.72. Therefore, for you can expect on average 3.5 probes per collision. Because this estimate does not vary based on the number of items in the Hashtable, the asymptotic access time for a Hashtable is O (1), which beats the pants off of the O ( n ) search time for an array. Finally, realize that expanding the Hashtable is not an inexpensive operation. Therefore, if you have an estimate as to how many items youre Hashtable will end up containing, you should set the Hashtables initial capacity accordingly in the constructor so as to avoid unnecessary expansions. The System. Collections. Generic. Dictionary Class The Hastable is a loosely-typed data structure, because a developer can add keys and values to the Hashtable of any type. As weve seen with the List class, as well as variants on the Queue and Stack classes, with the introduction of Generics in the. NET Framework 2.0, many of the built-in data structures have been updated to provide type-safe versions using Generics. The Dictionary class is a type-safe Hashtable implementation, and strongly types both the keys and values. When creating a Dictionary instance, you must specify the data types for both the key and value, using the following syntax: Returning to our earlier example of storing employee information using the last four digits of the social security as a hash, we might create a Dictionary instance whose key was of type integer (the nine digits of an employees social security number), and whose value was of type Employee (assuming there exists some class Employee ): Once you have created an instance of the Dictionary object, you can add and remove items from it just like with the Hashtable class. Collision Resolution in the Dictionary Class The Dictionary class differs from the Hashtable class in more ways than one. In addition to being strongly-typed, the Dictionary also employs a different collision resolution strategy than the Hashtable class, using a technique referred to as chaining . Recall that with probing, in the event of a collision another slot in the list of buckets is tried. (With rehashing, the hash is recomputed, and that new slot is tried.) With chaining, however, a secondary data structure is utilized to hold any collisions. Specifically, each slot in the Dictionary has an array of elements that map to that bucket. In the event of a collision, the colliding element is prepended to the buckets list. To better understand how chaining works, it helps to visualize the Dictionary as a hashtable whose buckets each contain a linked list of items that hash to that particular bucket. Figure 11 illustrates how series of items that hash to the same bucket will form a chain on that bucket. Figure 11. Chain created by a series of items hashed to the same bucket The Dictionary in Figure 11 has eight buckets, drawn in yellow and running from the top down. A number of Employee objects have been added to the Dictionary. When an Employee object is added to the Dictionary, it is added to the bucket that its key hashes to and, if theres already an Employee instance there, its prepended to the list of Employee s. That is, employees Al and John hash to the same bucket, as do Fred, Zak, and Amy, and Sam and Ted. Rather than reprobing in the event of a collision, as is done with the Hashtable class, the Dictionary simply chains any collisions onto the buckets list. Note In our discussions on the Hashtable class, I mentioned that the average asymptotic running time for adding, removing, and searching the hashtable using probing is constant time, O(1). Adding an item to a hashtable that uses chaining takes constant time as well because it involves only computing the items hash and prepending it to the appropriate buckets list. Searching and removing items from a chained hashtable, however, take, on average, time proportional to the total number of items in the hashtable and the number of buckets. Specifically, the running time is O(n m), where n is the total number of elements in the hashtable and m is the number of buckets. The Dictionary class is implemented such that n m at all times. That is, the sum of all chained elements can never exceed the number of buckets. Because n never exceeds m, searching and removing run in constant time as well. Conclusion In this article we examined four data structures with inherent class support in the. NET Framework Base Class Library: The Queue The Stack The Hashtable The Dictionary The Queue and Stack provide List - like capabilities in that they can store an arbitrary number of elements. The Queue and Stack differ from the List in the sense that while the List allows direct, random access to its elements, both the Queue and Stack limit how elements can be accessed. The Queue uses a FIFO strategy, or first in, first out. That is, the order with which items can be removed from the Queue is precisely the order with which they were added to the Queue. To provide these semantics, the Queue offers two methods: Enqueue() and Dequeue(). Queues are useful data structures for job processing or other tasks where the order with which the items are processed is based by the order in which they were received. The Stack, on the other hand, offers LIFO access, which stands for last in, first out. Stacks provide this access scheme through its Push() and Pop() methods. Stacks are used in a number of areas in computer science, from code execution to parsing. The final two data structure examined were the Hashtable and Dictionary. The Hashtable extends the ArrayList by allowing items to be indexed by an arbitrary key, as opposed to indexed by an ordinal value. If you plan on searching the array by a specific unique key, it is much more efficient to use a Hashtable instead, as the lookups by key value occur in constant time as opposed to linear time. The Dictionary class provides a type-safe Hashtable, with an alternate collision resolution strategy. This completes the second installment of this article series. In the third part well look at binary search trees, a data structure that provides O (log n ) search time. Like Hashtables, binary search trees are an ideal choice over arrays if you know you will be searching the data frequently. Until next time, Happy Programming References Cormen, Thomas H. Charles E. Leiserson, and Ronald L. Rivest. Introduction to Algorithms. MIT Press. 1990. Headington, Mark R. and David D. Riley. Data Abstraction and Structures Using C. D. C. Heath and Company. 1994. Richter, Jeffrey. Applied Microsoft. NET Framework Programming. Microsoft Press. 2002. Shared Source Common Language Infrastructure 1.0 Release. Microsoft - microsoft downloads details. aspxFamilyId3A1C93FA-7462-47D0-8E56-8DD34C6292F0ampdisplaylangen. Made available: November, 2002. Scott Mitchell . author of six books and founder of 4GuysFromRolla, has been working with Microsoft Web technologies since January 1998. Scott works as an independent consultant, trainer, and writer, and holds a Masters degree in Computer Science from the University of California San Diego. He can be reached at mitchell4guysfromrolla. or via his blog at ScottOnWriting. NET .

No comments:

Post a Comment