web-dev-qa-db-de.com

Wahl zwischen std :: map und std :: unordered_map

Nun, da std eine echte Hash-Map in unordered_map Hat, warum (oder wann) würde ich immer noch den guten alten map über unordered_map Auf Systemen verwenden wollen wo gibt es das eigentlich Gibt es offensichtliche Situationen, die ich nicht sofort sehen kann?

129
Johann Gerell

Wie bereits erwähnt , erlaubt map das sortierte Durchlaufen der Elemente, unordered_map Jedoch nicht. Dies ist in vielen Situationen sehr wichtig, beispielsweise beim Anzeigen einer Sammlung (z. B. eines Adressbuchs). Dies äußert sich auch auf andere indirekte Weise wie: (1) Starten Sie die Iteration von dem Iterator, der von find() zurückgegeben wird, oder (2) Vorhandensein von Mitgliedsfunktionen wie lower_bound().

Ich denke auch, dass es einen Unterschied in der Worst-Case-Komplexität search gibt.

  • Für map ist es O (lg N)

  • Für unordered_map Ist es O (N) [Dies darf, wenn die Hash-Funktion nicht gut ist und zu zu vielen Hash-Kollisionen führt.]

Gleiches gilt für worst case Streichung Komplexität.

108
Arun

Zusätzlich zu den obigen Antworten sollten Sie auch beachten, dass nur weil unordered_map Eine konstante Geschwindigkeit ist (O(1)), dies nicht bedeutet, dass es schneller ist als map (of order log(N)). Die Konstante kann größer als log(N) sein, zumal N auf 2 begrenzt ist32 (oder 264).

Zusätzlich zu den anderen Antworten (map behält Ordnung bei und Hash-Funktionen können schwierig sein) kann es sein, dass map performanter ist.

Zum Beispiel in einem Programm, in dem ich für einen Blog-Beitrag lief , sah ich, dass für VS10 std::unordered_map Langsamer war als std::map ( obwohl boost::unordered_map schneller war als beide).

Performance Graph

Beachten Sie den 3. bis 5. Takt.

85
Motti

Dies liegt an Googles Chandler Carruth in seinem CppCon 2014 Vortrag

std::map ist (von vielen als nicht nützlich erachtet) für leistungsorientiertes Arbeiten: Wenn Sie einen O (1) -amortisierten Zugriff wünschen, verwenden Sie ein geeignetes assoziatives Array (oder mangels eines, std::unorderded_map); Wenn Sie einen sortierten sequenziellen Zugriff wünschen, verwenden Sie etwas, das auf einem Vektor basiert.

Ebenfalls, std::map ist ein ausgeglichener Baum; und man muss es unglaublich oft durchqueren oder neu ausbalancieren. Dies sind Cache-Killer- und Cache-Apokalypse-Operationen ... also sag einfach NEIN zu std::map.

Vielleicht interessieren Sie sich für this SO question für effiziente Hash-Map-Implementierungen.

(PS - std::unordered_map ist Cache-unfreundlich, da verknüpfte Listen als Buckets verwendet werden.)

25
einpoklum

Ich denke, es liegt auf der Hand, dass Sie den std::map, Den Sie benötigen, verwenden, um Elemente in der Karte in sortierter Reihenfolge zu durchlaufen.

Sie können es auch verwenden, wenn Sie einen Vergleichsoperator (der intuitiv ist) anstelle einer Hash-Funktion (die im Allgemeinen sehr unintuitiv ist) schreiben möchten.

21
Ken Bloom

Angenommen, Sie haben sehr große Tasten, möglicherweise große Zeichenfolgen. Um einen Hash-Wert für eine große Zeichenfolge zu erstellen, müssen Sie die gesamte Zeichenfolge von Anfang bis Ende durchgehen. Es dauert mindestens eine lineare Zeit bis zur Länge des Schlüssels. Wenn Sie jedoch nur einen Binärbaum mit dem Operator > Des Schlüssels durchsuchen, kann jeder Zeichenfolgenvergleich zurückgegeben werden, wenn die erste Nichtübereinstimmung gefunden wird. Dies ist normalerweise sehr früh für große Saiten.

Diese Argumentation kann auf die Funktion find von std::unordered_map Und std::map Angewendet werden. Wenn die Art des Schlüssels so ist, dass es länger dauert, einen Hash zu erzeugen (im Fall von std::unordered_map), Als um die Position eines Elements mit der binären Suche zu finden (im Fall von std::map) sollte es schneller sein, einen Schlüssel im std::map nachzuschlagen. Es ist ziemlich einfach, sich Szenarien vorzustellen, in denen dies der Fall wäre, aber ich glaube, dass sie in der Praxis ziemlich selten sind.

9
Martin G