Ich weiß, was der Unterschied zwischen unshift()
und push()
Methoden in JavaScript ist, aber ich frage mich, was der Unterschied in der zeitlichen Komplexität ist.
Ich nehme an, dass die push()
Methode O (1) ist, weil Sie nur ein Element am Ende des Arrays hinzufügen, aber ich bin mir nicht sicher unshift()
, ob die Methode alle anderen vorhandenen Elemente vorwärts "verschieben" muss, und ich nehme an das ist O (log n) oder O (n)?
javascript
arrays
time
push
complexity-theory
Dperitch
quelle
quelle
unshift
könnte die Zeit nahezu konstant sein, aber ich muss mich fragen, ob es sich lohnt, den normalen Array-Zugriff zu erschweren. Ich persönlich glaube nicht, dass ich jemals einen Anruf an geschrieben habeunshift
.Antworten:
Die JavaScript-Sprachspezifikation schreibt meines Wissens nicht die zeitliche Komplexität dieser Funktionen vor.
Es ist sicherlich möglich, eine Array-ähnliche Datenstruktur (O (1) Direktzugriff) mit O (1)
push
undunshift
Operationen zu implementieren . Das C ++std::deque
ist ein Beispiel. Eine Javascript-Implementierung, die C ++ - Deques verwendet, um Javascript-Arrays intern darzustellen, hätte daher O (1)push
undunshift
Operationen.Aber wenn Sie solche Zeitgrenzen garantieren müssen, müssen Sie Ihre eigenen rollen, wie folgt:
http://code.stephenmorley.org/javascript/queues/
quelle
push () ist schneller.
js>function foo() {a=[]; start = new Date; for (var i=0;i<100000;i++) a.unshift(1); return((new Date)-start)} js>foo() 2190 js>function bar() {a=[]; start = new Date; for (var i=0;i<100000;i++) a.push(1); return((new Date)-start)} js>bar() 10
Code-Snippet anzeigen
function foo() {a=[]; start = new Date; for (var i=0;i<100000;i++) a.unshift(1); return((new Date)-start)} console.log(foo()) function bar() {a=[]; start = new Date; for (var i=0;i<100000;i++) a.push(1); return((new Date)-start)} console.log(bar());
Aktualisieren
Das Obige berücksichtigt nicht die Reihenfolge der Arrays. Wenn Sie sie richtig vergleichen möchten, müssen Sie das Push-Array umkehren. Allerdings ist das Drücken und Rückwärtsfahren
10ms
für mich auf Chrom mit diesem Snippet immer noch schneller :var a=[]; var start = new Date; for (var i=0;i<100000;i++) { a.unshift(1); } var end = (new Date)-start; console.log(`Unshift time: ${end}`); var a=[]; var start = new Date; for (var i=0;i<100000;i++) { a.push(1); } a.reverse(); var end = (new Date)-start; console.log(`Push and reverse time: ${end}`);
quelle
push(...)
war 10% schneller. Ich hatte keinen so großen Unterschied zwischen Javascript-Engines erwartet. Huh! (Ich habe gerade festgestellt, dass dieses Q 2 Jahre alt ist und Safari einen langen Weg zurückgelegt hat, den ichSafari 7.1.6
für das MacPro 2014-Modell verwende.)push
mitshift
schneller alsunshift
mitpop
.unshift
dauert 13.0 8ms undpush
dauert 3msFür Leute, die neugierig auf die v8-Implementierung sind, ist hier die Quelle . Da
unshift
eine beliebige Anzahl von Argumenten verwendet wird, verschiebt sich das Array selbst, um alle Argumente aufzunehmen.UnshiftImpl
endet AufrufAddArguments
mit einemstart_position
vonAT_START
ihm zu diesem welche Tritteelse
Aussage// If the backing store has enough capacity and we add elements to the // start we have to shift the existing objects. Isolate* isolate = receiver->GetIsolate(); Subclass::MoveElements(isolate, receiver, backing_store, add_size, 0, length, 0, 0);
und bringt es zum
MoveElements
.static void MoveElements(Isolate* isolate, Handle<JSArray> receiver, Handle<FixedArrayBase> backing_store, int dst_index, int src_index, int len, int hole_start, int hole_end) { Heap* heap = isolate->heap(); Handle<BackingStore> dst_elms = Handle<BackingStore>::cast(backing_store); if (len > JSArray::kMaxCopyElements && dst_index == 0 && heap->CanMoveObjectStart(*dst_elms)) { // Update all the copies of this backing_store handle. *dst_elms.location() = BackingStore::cast(heap->LeftTrimFixedArray(*dst_elms, src_index)) ->ptr(); receiver->set_elements(*dst_elms); // Adjust the hole offset as the array has been shrunk. hole_end -= src_index; DCHECK_LE(hole_start, backing_store->length()); DCHECK_LE(hole_end, backing_store->length()); } else if (len != 0) { WriteBarrierMode mode = GetWriteBarrierMode(KindTraits::Kind); dst_elms->MoveElements(heap, dst_index, src_index, len, mode); } if (hole_start != hole_end) { dst_elms->FillWithHoles(hole_start, hole_end); } }
Ich möchte auch darauf hinweisen, dass v8 ein unterschiedliches Konzept hat,
element kinds
je nachdem, was das Array enthält. Dies kann auch die Leistung beeinträchtigen.Es ist schwer zu sagen, wie hoch die Leistung ist, da es ehrlich gesagt davon abhängt, welche Arten von Elementen übergeben werden, wie viele Löcher sich im Array befinden usw. Wenn ich dies genauer durchforste, kann ich vielleicht eine endgültige Antwort geben, aber im Allgemeinen gehe ich davon aus Da
unshift
mehr Platz im Array reserviert werden muss, können Sie im Allgemeinen davon ausgehen, dass es O (N) ist (wird abhängig von der Anzahl der Elemente linear skaliert), aber jemand korrigiert mich bitte, wenn ich falsch liege.quelle
imho es hängt von der Javascript-Engine ab ... wenn es eine verknüpfte Liste verwendet, sollte das Verschieben ziemlich billig sein ...
quelle
Eine Möglichkeit, Arrays sowohl mit schnellem Verschieben als auch mit Push zu implementieren, besteht darin, Ihre Daten einfach in die Mitte Ihres C-Level-Arrays zu stellen. So macht es Perl, IIRC.
Eine andere Möglichkeit besteht darin, zwei separate C-Level-Arrays zu verwenden, sodass Push-Anhänge an einen von ihnen und Unshift-Anhänge an den anderen angehängt werden. Es gibt keinen wirklichen Vorteil für diesen Ansatz gegenüber dem vorherigen, den ich kenne.
Unabhängig davon, wie es implementiert ist, dauert ein Push- oder Un-Shift-Vorgang O (1), wenn das interne Array auf C-Ebene über genügend freien Speicher verfügt. Andernfalls muss bei einer Neuzuweisung mindestens O (N) Zeit zum Kopieren der alten Daten benötigt werden in den neuen Speicherblock.
quelle