Kürzlich habe ich eine sehr gute Beschreibung des Sammlungsframeworks in einem J2EE-Buch gesehen und veröffentlicht, um es mit allen zu teilen. Es enthält Schnittstellen, Klassen und Algorithmen Im Folgenden finden Sie eine Beschreibung der einzelnen Komponenten.
Sammlungsschnittstelle
Collection ist die grundlegendste Collection-Schnittstelle. Eine Collection stellt eine Reihe von Objekten dar, also die Elemente der Collection. Einige Sammlungen erlauben identische Elemente, andere nicht. Manche sortieren, andere nicht. Das Java SDK stellt keine Klassen bereit, die direkt von Collection erben. Die vom Java SDK bereitgestellten Klassen sind alle „Unterschnittstellen“, die von Collection erben, wie z. B. List und Set.
Alle Klassen, die die Collection-Schnittstelle implementieren, müssen zwei Standardkonstruktoren bereitstellen: einen parameterlosen Konstruktor zum Erstellen einer leeren Collection und einen Collection-Parameterkonstruktor zum Erstellen einer neuen Collection. Die Eingabesammlung enthält dieselben Elemente. Der letztere Konstruktor ermöglicht dem Benutzer das Kopieren einer Sammlung.
Wie kann ich jedes Element in der Sammlung durchlaufen? Unabhängig vom tatsächlichen Typ der Sammlung unterstützt sie eine iterator()-Methode, die einen Iterator zurückgibt, mit dem nacheinander auf jedes Element in der Sammlung zugegriffen werden kann. Typische Verwendung ist wie folgt:
Kopieren Sie den Codecode wie folgt:
Iterator it =collection.iterator(); // Holen Sie sich einen Iterator
while(it.hasNext()) {
Object obj = it.next(); // Das nächste Element abrufen
}
Die beiden von der Collection-Schnittstelle abgeleiteten Schnittstellen sind List und Set.
Listenschnittstelle List ist eine geordnete Sammlung. Mit dieser Schnittstelle können Sie die Einfügeposition jedes Elements genau steuern. Benutzer können über den Index (die Position des Elements in der Liste, ähnlich einem Array-Index), der einem Java-Array ähnelt, auf Elemente in der Liste zugreifen.
Im Gegensatz zum unten erwähnten Set erlaubt List die gleichen Elemente.
Zusätzlich zur iterator()-Methode, die für die Collection-Schnittstelle erforderlich ist, bietet List auch eine listIterator()-Methode, die eine ListIterator-Schnittstelle zurückgibt. Im Vergleich zur Standard-Iterator-Schnittstelle verfügt ListIterator über einige weitere add()- und andere Methoden, die Ergänzungen ermöglichen. Elemente löschen, festlegen und vorwärts oder rückwärts bewegen.
Gängige Klassen, die die List-Schnittstelle implementieren, sind LinkedList, ArrayList, Vector und Stack.
LinkedList-Klasse LinkedList implementiert die List-Schnittstelle und lässt Nullelemente zu. Darüber hinaus bietet LinkedList zusätzliche Get-, Remove- und Insert-Methoden am Anfang oder Ende von LinkedList. Diese Operationen ermöglichen die Verwendung von LinkedList als Stack, Warteschlange oder Deque.
Beachten Sie, dass LinkedList keine synchronisierten Methoden hat. Wenn mehrere Threads gleichzeitig auf eine Liste zugreifen, müssen sie die Zugriffssynchronisierung selbst implementieren. Eine Problemumgehung besteht darin, beim Erstellen der Liste eine synchronisierte Liste zu erstellen:
Liste list = Collections.synchronizedList(new LinkedList(...));
ArrayList-Klasse ArrayList implementiert Arrays variabler Größe. Es erlaubt alle Elemente, einschließlich null. ArrayList ist nicht synchronisiert.
Die Laufzeit der Methoden size, isEmpty, get und set ist konstant. Allerdings sind die Kosten der Add-Methode eine amortisierte Konstante, und das Addieren von n Elementen erfordert O(n) Zeit. Andere Methoden haben eine lineare Laufzeit.
Jede ArrayList-Instanz verfügt über eine Kapazität (Capacity), die der Größe des Arrays entspricht, das zum Speichern von Elementen verwendet wird. Diese Kapazität erhöht sich automatisch, wenn neue Elemente hinzugefügt werden, der Wachstumsalgorithmus ist jedoch nicht definiert. Wenn eine große Anzahl von Elementen eingefügt werden muss, kann die Methode „sichsureCapacity“ aufgerufen werden, um die Kapazität der ArrayList vor dem Einfügen zu erhöhen und so die Einfügeeffizienz zu verbessern.
Wie LinkedList ist auch ArrayList nicht synchronisiert.
Vektorklasse Vector ist ArrayList sehr ähnlich, aber Vector ist synchronisiert. Obwohl der von Vector erstellte Iterator dieselbe Schnittstelle hat wie der von ArrayList erstellte Iterator, weil Vector synchronisiert ist, ändert ein anderer Thread den Status des Vectors, wenn ein Iterator erstellt und verwendet wird (z. B. indem er ein Element hinzufügt oder entfernt). , ConcurrentModificationException wird beim Aufruf der Iterator-Methode ausgelöst, daher muss die Ausnahme abgefangen werden.
Stack-Klasse Stack erbt von Vector und implementiert einen Last-In-First-Out-Stack. Stack bietet 5 zusätzliche Methoden, mit denen Vector als Stack verwendet werden kann. Die grundlegenden Push- und Pop-Methoden sowie die Peek-Methode holen sich das Element oben im Stapel, die leere Methode testet, ob der Stapel leer ist, und die Suchmethode erkennt die Position eines Elements im Stapel. Der Stapel ist nach seiner Erstellung ein leerer Stapel.
Schnittstelle einstellen Set ist eine Sammlung, die keine doppelten Elemente enthält, d. h. zwei beliebige Elemente e1 und e2 haben e1.equals(e2)=false und Set hat höchstens ein Nullelement.
Offensichtlich unterliegt der Set-Konstruktor der Einschränkung, dass der übergebene Collection-Parameter keine doppelten Elemente enthalten darf.
Bitte beachten Sie: Mit veränderlichen Objekten muss vorsichtig umgegangen werden. Wenn ein veränderliches Element in einem Set seinen Zustand ändert und Object.equals(Object)=true verursacht, führt dies zu einigen Problemen.
Map-Schnittstelle <BR>Bitte beachten Sie, dass Map nicht die Collection-Schnittstelle erbt, die den Schlüssel zur Wertzuordnung bereitstellt. Eine Map kann nicht denselben Schlüssel enthalten und jeder Schlüssel kann nur einen Wert zuordnen. Die Map-Schnittstelle bietet drei Arten von Satzansichten. Der Inhalt der Karte kann als Satz von Schlüsselsätzen, Satz von Wertsätzen oder Satz von Schlüsselwertzuordnungen betrachtet werden.
Hashtable-Klasse Hashtable erbt die Map-Schnittstelle und implementiert eine Hash-Tabelle für die Schlüsselwertzuordnung. Als Schlüssel oder Wert kann jedes Nicht-Null-Objekt verwendet werden.
Um Daten hinzuzufügen, verwenden Sie put(key, value) und um Daten zu entfernen, verwenden Sie get(key). Der Zeitaufwand dieser beiden Grundoperationen ist konstant.
Hashtable passt die Leistung über zwei Parameter an: Anfangskapazität und Auslastungsfaktor. Normalerweise erreicht der Standardlastfaktor 0,75 ein besseres Gleichgewicht zwischen Zeit und Raum. Durch Erhöhen des Auslastungsfaktors kann Platz gespart werden, die entsprechende Suchzeit erhöht sich jedoch, was sich auf Vorgänge wie „Abrufen“ und „Put“ auswirkt.
Ein einfaches Beispiel für die Verwendung von Hashtable ist wie folgt: Geben Sie 1, 2 und 3 in die Hashtable ein, und ihre Schlüssel sind „eins“, „zwei“ und „drei“:
Kopieren Sie den Codecode wie folgt:
Hashtable-Nummern = new Hashtable();
zahlen.put(“one”, new Integer(1));
zahlen.put(“two”, new Integer(2));
zahlen.put(“drei”, new Integer(3));
Um eine Zahl wie 2 abzurufen, verwenden Sie die entsprechende Taste:
Ganzzahl n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);
Da das als Schlüssel verwendete Objekt die Position des entsprechenden Werts durch Berechnung seiner Hash-Funktion bestimmt, muss jedes als Schlüssel verwendete Objekt die Methoden hashCode und equal implementieren. Die Methoden hashCode und equal erben von der Stammklasse Object. Wenn Sie eine benutzerdefinierte Klasse als Schlüssel verwenden, müssen Sie gemäß der Definition der Hash-Funktion sehr vorsichtig sein, wenn die beiden Objekte gleich sind, d. h. obj1.equals( obj2)=true, dann muss ihr HashCode sein Das Gleiche, aber wenn zwei Objekte unterschiedlich sind, ist ihr HashCode nicht unbedingt unterschiedlich. Wenn der HashCode zweier verschiedener Objekte gleich ist, wird dieses Phänomen als Konflikt bezeichnet. Der Zeitaufwand für den Betrieb der Hash-Tabelle erhöht sich Um es gut zu definieren, kann die Methode hashCode() Hash-Tabellenoperationen beschleunigen.
Wenn dasselbe Objekt einen anderen Hash-Code hat, führt die Operation der Hash-Tabelle zu unerwarteten Ergebnissen (die erwartete Get-Methode gibt null zurück. Um dieses Problem zu vermeiden, müssen Sie sich nur eines merken: Überschreiben Sie gleichzeitig die Equals-Methode und die HashCode-Methode). Zeit. Schreiben Sie nicht nur eine davon.
Hashtable ist synchron.
HashMap-Klasse HashMap ähnelt Hashtable, mit der Ausnahme, dass HashMap asynchron ist und Nullen zulässt, d. h. Nullwerte und Nullschlüssel. , aber wenn HashMap als Sammlung behandelt wird (die Methode „values()“ kann eine Sammlung zurückgeben), ist der Zeitaufwand ihrer Iterationsunteroperationen proportional zur Kapazität der HashMap. Wenn die Leistung iterativer Operationen sehr wichtig ist, sollten Sie daher die Anfangskapazität von HashMap nicht zu hoch oder den Auslastungsfaktor zu niedrig einstellen.
WeakHashMap-Klasse WeakHashMap ist eine verbesserte HashMap, die „schwache Referenzen“ auf Schlüssel implementiert. Wenn ein Schlüssel nicht mehr extern referenziert wird, kann der Schlüssel von GC recycelt werden.
Zusammenfassung <BR>Wenn Vorgänge wie Stapel und Warteschlangen beteiligt sind, sollten Sie die Verwendung von List in Betracht ziehen. Wenn Sie Elemente schnell einfügen und löschen müssen, sollten Sie LinkedList verwenden. Wenn Sie schnellen Direktzugriff auf Elemente benötigen, sollten Sie ArrayList verwenden.
Wenn sich das Programm in einer Single-Thread-Umgebung befindet oder der Zugriff nur in einem Thread erfolgt, sollten Sie asynchrone Klassen in Betracht ziehen, die effizienter sind. Wenn mehrere Threads eine Klasse gleichzeitig betreiben können, sollten synchronisierte Klassen verwendet werden.
Achten Sie besonders auf die Funktionsweise der Hash-Tabelle. Das als Schlüssel verwendete Objekt muss die Methoden equal und hashCode korrekt überschreiben.
Versuchen Sie, die Schnittstelle und nicht den tatsächlichen Typ zurückzugeben, z. B. List anstelle von ArrayList, sodass der Clientcode nicht geändert werden muss, wenn Sie ArrayList in Zukunft durch LinkedList ersetzen müssen. Das ist Programmierung zur Abstraktion.