Verschachtelte Karten vs. kombinierte Schlüssel


8

In dem Projekt, an dem ich gerade arbeite, hatten wir drei verschiedene Arten von Preisen, abhängig vom Alter des Benutzers (Erwachsener, Kind usw.). Wir hatten also in der DB einen Tisch, der so aussah:

PRICES
type     Amount
A         20
B         15
C         ..
D         ..

Anfangs hatten wir nur 4 verschiedene Arten von Preisen, also hatten wir im Code so etwas:

Map<String, BigDecimal> prices = new HashMap<String, BigDecimal>();

Wo die Schlüssel waren die Preisart.

Kürzlich haben sie eine neue Geschäftsregel hinzugefügt, die jedem Preistyp drei Untertypen hinzufügt. Jetzt haben wir so etwas:

PRICES
type   subtype  Amount
A          1      20
A          2      15
A          3      ..
B          1      ..
B          2      ..
...        ..     ..

Welche der beiden folgenden Optionen ist Ihrer Meinung nach besser und warum?

Verschachtelte Karten

Map<String, Map<String, BigDecimal>> prices;

Dabei sind die Schlüssel der Preistyp und der Subtyp:

prices.get(type).get(subtype);

Kombinierte Schlüssel

Dieselbe Karte wie ursprünglich:

Map<String, BigDecimal> prices;

Und verketten Sie die Schlüssel, um die verschiedenen Preise zu indizieren:

prices.get(type+"_"+subtype);

Dies ist eher eine Entwurfsfrage, und es gibt nicht genügend Code für die Codeüberprüfung.
200_erfolg

Ein kleiner Hinweis: Ihr vorgeschlagener kombinierter Schlüssel kann später zu Problemen führen, z. B. wenn Sie ein '_' in einem Preistyp benötigen. Erwägen Sie stattdessen einen class PriceKey{ PriceType type; PriceSubtype subtype; }Schlüssel. Dies kann dann leicht weiter verlängert werden
Caleth

Antworten:


8

Sowohl verschachtelte als auch kombinierte Schlüssel haben ihre Plätze. bowmore gibt ein Pro-Argument für zusammengesetzte Schlüssel und ein Con-Argument für verschachtelte Karten an. Lassen Sie mich die loyale Opposition liefern:

Zusammengesetzte Kartenschlüssel eignen sich hervorragend, wenn Sie nach einem bestimmten bekannten Objekt suchen.

Verschachtelte Karten funktionieren gut, wenn Sie schnell alle Variationen, Arten und Untertypen des Typs A finden müssen. Die Auswahl von A (vs. B, C, ...) kann beispielsweise der erste Schritt in einem Entscheidungsbaum sein. Sobald der Benutzer oder Algorithmus oder was auch immer A auswählt, müssen Sie nur über die Subtypen von A Bescheid wissen, und B..Z oder B..ZZZZZ spielen keine Rolle mehr.

Jetzt haben Sie es mit einer sehr engen und effizienten Suchstruktur für die Subsuche zu tun. Wenn Sie dies mit zusammengesetzten Schlüsseln versuchen, führen Sie am Ende einen vollständigen Tabellenscan a la durch [ (key, value) for (key, value) in prices.items() if key.startswith('A') ]. Dies ist keine effiziente Operation und wird langsam sein, wenn die Karte überhaupt groß ist.

Verschachtelte Karten funktionieren auch gut, wenn die Anzahl der Verschachtelungsebenen zunimmt. Die Problemstruktur wurde bereits von typebis erweitert (type, subtype). Gibt es eine Chance, die die nächste Umdrehung braucht (type, subtype, variation)oder (type, subtype, version)? In diesem Fall kann ein verschachtelter Zuordnungsansatz sauber erweitert werden. Dies ist jedoch ein stilistischer Vorteil zweiter Ordnung, insbesondere im Vergleich zu dem oben genannten Vorteil der "einfachen Subsuche".


Um ehrlich zu sein, kann die inkrementelle Suche nach zusammengesetzten Schlüsseln mithilfe einer SortedMap einen "Tabellenscan" vermeiden.
Bowmore

Sie können die Sucheigenschaften für zusammengesetzte Schlüssel verbessern, indem Sie die Karte sortieren. Sie können jedoch nicht vermeiden, dass alle auf diese Weise gescannt werden. Um beispielsweise den Schlüssel "RXR_1_a" in einer Tabelle oder eine sortierte Zuordnung von 10.000 Schlüsseln zu finden, muss noch der Tabellenindex gefunden werden, in dem "RXR_ *" beginnt und endet. Selbst bei der binären Suche ist dies keine Nullzeit. Es erfordert auch die gleichen key.startswith('RXR')Tests, die ich erwähnt habe. Verschachtelte Karten sind immer O (1) -Operationen und erfordern weder den Aufwand für das Vorsortieren der Karte noch für Zeichenfolgenvergleiche.
Jonathan Eunice

Das ist richtig, ich habe nur gesagt, Sie könnten eine Operation im Stil eines Tabellenscans vermeiden. In meiner Antwort weise ich auch darauf hin, dass es für diese Art der Suche einfacher ist, die Datenbank direkt abzufragen. Beide Ansätze haben Probleme bei der Suche nach einem Teilschlüssel, der nicht beim ersten Feld beginnt, und eine Datenbank kann dies durch zusätzliche Indizes verringern.
Bowmore

Eine SortedMap kann eine vollständige lineare Suche vermeiden, jedoch keine kostenintensive Suche. Ich sehe nicht, wie es hilft, die Operation in einer Datenbank zu verpfänden. Dies kann den Clientcode möglicherweise vereinfachen, jedoch auf Kosten langsamer externer Anforderungen und zusätzlicher Datenstrukturen (die Indizes, die erforderlich sind, um den Vorgang noch einigermaßen effizient zu gestalten). Eine verschachtelte Struktur hat zwar einige Nachteile, ist jedoch eigenständig und hocheffizient, entweder mit oder ohne Unterstützung für Sicherungsdatenbanken.
Jonathan Eunice

Wie ist eine binäre Suche eine kostenintensive Suche?
Bowmore

6

Vermeiden Sie verschachtelte Karten. Sie sind schwieriger zu skalieren und führen zu Code, der sehr ausführlich und schwer zu lesen ist, wenn alle verschachtelten Generika-Deklarationen ausgeführt werden.

Noch wichtiger ist, dass Maps in Java viel Speicherplatz beanspruchen. Wenn Sie eine Karte mit noch mehr Karten füllen, wird das Problem des Speicherverbrauchs nur noch verschlimmert.

Schließlich ist eine Karte, die zusammengesetzte Schlüssel verwendet, leichter zu überlegen.

Die Verwendung von zusammengesetzten Schlüsseln erleichtert Ihnen in den meisten Fällen das Leben, einige Dinge werden jedoch schwieriger. Sie erhalten beispielsweise alle Preise für eine bestimmte Schlüsselkomponente, aber es ist wahrscheinlicher, dass Sie dieses Ergebnis direkt aus der Datenbank abfragen, als es aus der Karte zu destillieren.


Ich stimme Ihrer Aussage zu, jedoch sind verschachtelte Karten manchmal unvermeidbar, und in diesem Szenario habe ich keine gute Möglichkeit gefunden, sie in Java zu bearbeiten. Wann immer wir etwas Allgemeines in Java lesen / bearbeiten müssen, insbesondere JSON-Dateien, benötigen Sie verschachtelte Maps, es sei denn, Sie möchten die Struktur Ihrer Datei in Ihrer Anwendung fest codieren.
Froginvasion

1

Dies hat weniger mit "welche Implementierung am besten ist" als vielmehr mit "mit welcher Abstraktion soll ich arbeiten" zu tun.

Sowohl der zusammengesetzte Schlüssel als auch die Karte der Karten haben ihre Stärken und Schwächen, die alle im Bereich der Leistung liegen (dh Geschwindigkeit / Speichernutzung). Sie unterscheiden sich nicht in ihrer Funktionalität: Beide nehmen zwei Werte an und geben den zuvor "put" -Wert zurück.

Da sie funktional gleichwertig sind, sollten Sie sie zuerst abstrahieren. Mach dir keine Sorgen, welches besser ist. Erstellen Sie eine DoubleKeyedMap-Schnittstelle mit allen erforderlichen Methoden und verwenden Sie diese in Ihrem Code. Schreiben Sie dann die Implementierung, die Sie am schnellsten schreiben können, und fahren Sie einfach fort.

NUR wenn Sie Ihre Anwendung geschrieben haben und festgestellt haben, dass Ihre Implementierung des zusammengesetzten Schlüssels nicht sehr schnell nach dem ersten Schlüssel filtert oder dass die Karte der Karten zu viel Speicherplatz beansprucht, wenn Sie sie optimieren.

Vorzeitige Optimierung ist die Wurzel allen Übels. Nicht abstrahieren ist schlimmer.


Gut gesagt. Nicht abstrahieren ist schlimmer! Ich liebe das
Zack Bartel

0

Beide Optionen sind meiner Meinung nach nicht gut. Was ist, wenn sich die Geschäftslogik erneut ändert, sodass Sie einen anderen Subtyp haben?

Ich schlage vor, dass Sie Folgendes tun:

  1. Verwenden Sie einen Ersatzschlüssel für einen Tabellenaufruf, wie Typediese Tabelle aussehen würdeType(INT auto_inc id, VARCHAR(255) name, VARCHAR(255) desc, INT status, etc)
  2. PriceVerwenden Sie in Ihrer Tabelle einen Fremdschlüssel für die Typeobige Tabelle, damit es so aussiehtPrice(INT type_id, INT price)
  3. Stellen Sie sicher, dass Sie das Löschen eines Typs NICHT unterstützen. Sie markieren einfach einen Typ, inactiveda das Löschen aufgrund der baumelnden Referenzen zu echten Kopfschmerzen führen würde

Jetzt können Benutzer- und Geschäftslogik dem Schema eine beliebige Typ-Subtyp-Kombination hinzufügen. Sie müssten lediglich eine neue Zeile in der TypeTabelle erstellen und diese nameund / oder descdie alte Tabelle ändern .

In Ihrem Fall würde der Benutzer den Typ Ain Typ umbenennen A_1, einen neuen Typ namens hinzufügen A_2und einen neuen Preis für A_2as festlegen15

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.