Was ist effizienter : mit removeAll() oder mithilfe der folgenden HashMap Technik zu behalten, nur die geänderten Datensätze in eine ArrayList
Ich habe 2 ArrayList
s A
und B
von der gleichen datastructure C
(hashCode() und equals() überschrieben). C repräsentiert einen Studenten-Datensatz. Die beiden Listen sind von der gleichen Größe und stellen neue Schüler-Datensätze und alten bzw. (die Schüler sind die gleichen in beiden Listen, Bestellung könnte anders sein). Ich möchte Euch nur die Datensätze in Eine, die geändert wurden. Als solche, ich weiß :
A.removeAll(B)
Gemäß den javadocs, diese hätte jeder Datensatz Ein und vergleichen Sie diese mit jedem Datensatz von B, und wenn es feststellt, dass beide gleich, wird Sie den Rekord von A. Wenn ein Datensatz von A ist nicht gleich zu jedem Datensatz in B, und da alle Studenten, die in A sind auch in B, es bedeutet, dass die Aufzeichnung Eines hat sich geändert. Das problem ist, dass es leicht von n quadratische Komplexität.
Kann ein anderer Ansatz sein :
Map<C> map = new HashMap<C>();
for (C record : B){
map.add(record.getStudentId(),record);
}
List<C> changedRecords = new ArrayList<C>();
for (C record : A){
if (record.equals(map.get(record.getStudentId())){
changedRecords.add(record);
}
}
Ich denke, das könnte der eine niedrigere Komplexität als die obige Lösung. Ist das richtig ?
- Vergessen Sie Ihre Leistung, Ihr original-Lösung ist weit mehr lesbar ist. Nur wenn es sich herausstellt, zu einem Engpass kommen, sollten Sie sogar überlegen, die zweite.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ja der letztere Algorithmus ist besser als
O(n^2)
, da hast du zwei Schleifen, eine bis überB
und anderen überA
- und Sie tun (amortisiert) Konstante Arbeit in jeder Schleife, die neue Lösung läuft inO(|A| + |B|)
.Ich vermute, dass Sie nicht alle doppelten Einträge aber. Wenn dies der Fall ist, können Sie auch gehen über eine
HashSet
(änderungLinkedHashSet
wenn Sie wollen, um die Erhaltung der Reihenfolge, inA
):(Oder, wenn die Reihenfolge egal, Sie könnte verwenden
HashSet
s den ganzen Weg durch.)Wie bereits von @Daud in den Kommentaren unten
HashSet.removeAll(Collection c)
ruft tatsächlichc.contains
wiederholt, wenn die Größe der hash-set ist kleiner als die Sammlung, die wirkt sich auf die Komplexität (zumindest in OpenJDK). Dies ist, da die Implementierung wählt immer die Iteration über die kleineren Sammlung.Was Sie speichern können, die auf Komplexität, Sie könnten verlieren in Speicherzuordnung, so ist nicht unbedingt effizienter. Arrraylist verwendet etwas ähnliches, um eine in-place-Partitionierung-Algorithmus zu heruntergekommen die backing-array und test gegen die vergleichen.
Beim Vergleich es sieht einfach zu finden, den index des ersten Auftretens von einem match gegen den backing-array
Object[]
. Der Algorithmus verwaltet zwei Indizes, eine für die Iteration durch die backing-array und einem als Platzhalter für die Spiele. Im Fall einer übereinstimmung, es bewegt sich einfach den index auf der Rückseite array und führt Sie auf die nächste eingehende element; dies ist relativ Billig.Kommt es zu einem Punkt, wo Sie feststellt, dass die eingehende Sammlung enthält nicht den Wert am aktuellen index in der backing-array es einfach überschreibt das element, wo das Letzte match trat mit dem element am aktuellen index, ohne dass eine neue Speicherzuweisung. Dieses Muster wird wiederholt, bis alle Elemente in der ArrayList wurde ein Vergleich mit der eingehenden Inkasso zugrunde, daher die Komplexität, die Sie sind besorgt über.
Zum Beispiel:
Betrachten Sie eine arraylist mit 1,2,4,5 und eine Sammlung 'C' mit 4,1, dass wir das match gegen; zu wollen, entfernen Sie 4 und 1. hier ist jeder iteration auf die for-Schleife, das würde gehen 0 -> 4
Iteration: r ist die for-Schleife den index auf arraylist ein
for (; r < size; r++)
r = 0 (nicht C enthalten 1? Ja, fahren Sie mit dem nächsten)
A: 1,2,4,5 w = 0
r = 1 (Nicht C enthalten 2? Nein, kopieren Sie den Wert in r in dem spot gezeigt, durch w++)
A: 2,2,4,5 w=1
r = 2 (Nicht C enthalten 4?, Ja überspringen)
A: 2,2,4,5 w=1
r = 3 (Keine C-5 enthalten? Nein, kopieren Sie den Wert in r in dem spot gezeigt, durch w++)
A: 2,5,4,5 w=2
r=4, stop
Vergleichen Sie w, um die Größe der backing-array-4. Da sind Sie nicht gleich Null die Werte von w auf das Ende des Arrays, und setzen Sie die Größe.
A: 2,5 Größe von 2
Den eingebauten removeAll auch der Auffassung, dass die ArrayLists kann null enthalten. Sie könnten werfen eine NPE auf Rekord.getStudentId() in der Lösung oben. Schließlich removeAll schützt gegen Ausnahmen in der Vergleichs-Sammlung auf.enthält. wenn das passiert, nutzt es schließlich zu tun, eine native memcopy schützt die Unterlage array von Korruption in einer hoch effizienten Art und Weise.
Definitiv zweite 'Algorithmus' ist besser als die erste berücksichtigt amortisierten Analyse. ist es der beste Weg? brauchen Sie das? verursacht es irgendwelche sichtbaren Auswirkungen für Anwender in Bezug auf Leistung
nicht die Anzahl der Elemente in Liste wachsen so groß, dass dies zu einem Engpass im system?
Erste Ansatz ist mehr lesbar, vermittelt Sie Ihre Absicht, Menschen, die Pflege des Codes. Auch ist es vorzuziehen, verwenden Sie 'getestet' API, anstatt neu zu erfinden das Rad (wenn unbedingt notwendig)
Computer sind inzwischen so schnell, dass wir nicht tun sollten jede vorzeitige Optimierungen.
gesehen wichtig, dass ich gehen könnte, mit einer Lösung, die mithilfe von Set, ähnlich wie @aioob ist
Dem ich begegnet bin, einem performance-Engpass in den removeAll in einigen Fällen (EMF-Modell manipulation Verwandte). Für
ArrayList
wie die oben genannten, verwenden Sie einfach standard -removeAll
, aber wenn Ein ist beispielsweise ein EList, n^2 auftreten können.Daher vermeiden, sich auf verborgene gute Eigenschaften von bestimmten Implementierungen
List< T >
;Set.contains()
O(1) eine Garantie (wenn SieHashSet
und haben eine anständige hashCode, log2(n) - fürTreeSet
Zusammenhang mit der Bestellung), verwenden, um gebunden Algorithmische Komplexität.Ich den folgenden code verwenden, vermeidet sinnlose Kopien; die Absicht ist, dass Sie zum Scannen eine Datenstruktur, die die Suche irrelevante Elemente, die Sie nicht wollen, und indem Sie "todel".
Für einige Grund wie die Vermeidung von gleichzeitigen änderungen, die Sie navigieren in einem Baum etc..., können Sie entfernen von Elementen, wie Sie dies tun, aktualisiert. So, wir kumulieren, die Sie in ein HashSet "todel".
In der Funktion, das müssen wir ändern "container", da ist es in der Regel ein Attribut des Aufrufers, sondern mit remove(int index) auf "container" dazu bewegen könnten, eine Kopie, da der Links-Verschiebung der Elemente. Wir benutzen eine " kopieren "Inhalt" um dies zu erreichen.
Template-argument ist, da während der Auswahl-Prozess, bekomme ich oft Subtypen C, aber fühlen Sie sich frei zu verwenden < T > und das überall.
Also in deinem Fall würde man aufrufen mit :
removeAll(A, new HashSet < C >(B));
Zahlung eine Kopie von B, wenn Sie wirklich können sich nicht ansammeln, in einen Satz< C > während der phase der Auswahl.
Legen Sie es in eine utility-Klasse und static-import für einfache Bedienung.