Ich hole eine Reihe von Tupeln aus der Datenbank und füge sie in eine Karte ein. Die Datenbankabfrage ist kostspielig.
Es gibt keine offensichtliche natürliche Reihenfolge der Elemente in der Karte, aber die Reihenfolge der Einfügungen ist dennoch wichtig. Das Sortieren der Karte wäre sehr aufwändig, daher möchte ich dies vermeiden, da das Abfrageergebnis bereits so sortiert ist, wie ich es möchte. Daher speichere ich das Abfrageergebnis einfach in a LinkedHashMap
und gebe die Zuordnung von einer DAO-Methode zurück:
public LinkedHashMap<Key, Value> fetchData()
Ich habe eine Methode processData
, die eine gewisse Verarbeitung auf der Karte durchführen sollte - einige Werte ändern, einige neue Schlüssel / Werte hinzufügen. Es ist definiert als
public void processData(LinkedHashMap<Key, Value> data) {...}
Mehrere Linters (Sonar usw.) beschweren sich jedoch, dass die Art der "Daten" eine Schnittstelle wie "Karte" sein sollte und nicht die Implementierung "LinkedHashMap" ( Squid S1319 ).
Also heißt es im Grunde, dass ich haben sollte
public void processData(Map<Key, Value> data) {...}
Aber ich möchte, dass die Methodensignatur sagt, dass die Kartenreihenfolge wichtig ist - es kommt auf den Algorithmus an processData
-, damit meine Methode nicht nur eine zufällige Karte übergibt.
Ich möchte es nicht verwenden SortedMap
, da es (aus dem Javadoc vonjava.util.SortedMap
) "nach der natürlichen Reihenfolge seiner Schlüssel oder von einem Komparator geordnet ist, der normalerweise zum Zeitpunkt der Erstellung der sortierten Karte bereitgestellt wird."
Meine Schlüssel haben keine natürliche Reihenfolge , und die Erstellung eines Komparators , um nichts zu tun, scheint ausführlich zu sein.
Und ich würde immer noch wollen, dass es eine Karte ist, put
um doppelte Schlüssel usw. zu vermeiden. Wenn nicht, data
hätte es eine sein können List<Map.Entry<Key, Value>>
.
Wie sage ich also, dass meine Methode eine Karte haben möchte, die bereits sortiert ist ? Leider gibt es keine java.util.LinkedMap
Schnittstelle, sonst hätte ich das benutzt.
quelle
if you are new to programming and stumble upon this answer, don't think this allows you to go against best practice because it doesn't.
- Guter Rat, wenn es so etwas wie "Best Practice" gäbe. Besser beraten: Erfahren Sie, wie Sie die richtigen Entscheidungen treffen. Folgen Sie der Praxis, wenn es Sinn macht, aber lassen Sie Werkzeuge und Behörden Ihren Denkprozess leiten, nicht diktieren.Du kämpfst gegen drei Dinge:
Erstens ist Java Container-Bibliothek. Nichts in seiner Taxonomie gibt Ihnen die Möglichkeit zu bestimmen, ob die Klasse in einer vorhersagbaren Reihenfolge iteriert oder nicht. Es gibt keine
IteratesInInsertedOrderMap
Schnittstelle, die implementiert werden könnteLinkedHashMap
, wodurch die Typprüfung (und die Verwendung alternativer Implementierungen, die sich auf die gleiche Weise verhalten) unmöglich wird. Das ist wahrscheinlich so gewollt, weil der Geist davon ist, dass man wirklich in der Lage sein soll, mit Objekten umzugehen, die sich wie das Abstrakte verhaltenMap
.Zweitens ist es eine Überzeugung, dass das, was deine Beschwörerin sagt, als Evangelium behandelt werden muss und dass es schlecht ist, alles zu ignorieren, was es sagt. Im Gegensatz zu dem, was heutzutage als bewährte Methode gilt, sollten Linter-Warnungen keine Barrieren für den Aufruf Ihres Codes darstellen. Sie werden aufgefordert, über den von Ihnen geschriebenen Code nachzudenken und anhand Ihrer Erfahrung und Ihres Urteils zu bestimmen, ob die Warnung gerechtfertigt ist oder nicht. Ungerechtfertigte Warnungen sind der Grund, warum fast jedes statische Analysetool einen Mechanismus bietet, mit dem Sie feststellen können, dass Sie den Code überprüft haben, Sie denken, was Sie tun, ist in Ordnung und sie sollten sich in Zukunft nicht darüber beschweren.
Drittens, und dies ist wahrscheinlich das Fleisch davon,
LinkedHashMap
kann das falsche Werkzeug für den Job sein. Karten sind für den zufälligen, nicht geordneten Zugriff vorgesehen. WennprocessData()
Sie die Datensätze einfach der Reihe nach durchlaufen und keine anderen Datensätze nach Schlüssel suchen müssen, erzwingen Sie eine bestimmte Implementierung vonMap
, um die Aufgabe eines zu erledigenList
. Auf der anderen SeiteLinkedHashMap
ist es das richtige Werkzeug , wenn Sie beides benötigen, da es bekanntermaßen das tut, was Sie wollen, und Sie mehr als berechtigt sind, es zu fordern .quelle
OrderedMap
, könnte ich das genauso gut sagenUniqueList
. Solange es sich um eine Sammlung mit einer definierten Iterationsreihenfolge handelt, werden beim Einfügen Duplikate überschrieben.Set
beim Erstellen der Liste immer nur eine temporäre Liste der Schlüssel erstellen, um sie zu erkennen.processData
ändert die Karte, einige Werte zu ersetzen, einige neue Schlüssel / Wert - Einführung. Könnte alsoprocessData
Duplikate einführen, wenn es auf etwas anderem als a operiertMap
.UniqueList
(oderOrderedUniqueList
) schreiben und diese verwenden. Es ist ziemlich einfach und macht Ihren Verwendungszweck klarer.Wenn Sie
LinkedHashMap
nur die Möglichkeit haben, Duplikate zu überschreiben, diese jedoch tatsächlich als verwendenList
, ist es empfehlenswert, diese Verwendung mit Ihrer eigenen benutzerdefiniertenList
Implementierung zu kommunizieren . Sie können es auf einem vorhandenen Java Sammlungen Klasse basieren und einfach jede außer Kraft setzenadd
undremove
Methoden , um Ihre Sicherungsspeicher zu aktualisieren und den Überblick über die Taste halten , um sicherzustellen , Einzigartigkeit. Wenn Sie diesem Namen einen eindeutigen NamenProcessingList
geben, wird deutlich, dass Argumente, die für IhreprocessData
Methode verwendet werden, auf eine bestimmte Art und Weise behandelt werden müssen.quelle
ProcessingList
als Alias für erstellen.LinkedHashMap
Sie können sie später jederzeit durch eine andere ersetzen, solange die öffentliche Schnittstelle intakt bleibt.Ich höre Sie sagen: "Ich habe einen Teil meines Systems, der eine LinkedHashMap erstellt, und in einem anderen Teil meines Systems muss ich nur LinkedHashMap-Objekte akzeptieren, die im ersten Teil erstellt wurden, da diejenigen, die mit einem anderen Verfahren erstellt wurden, gewonnen wurden." funktioniert nicht richtig. "
Aus diesem Grund denke ich, dass das Problem hier darin besteht, dass Sie LinkedHashMap verwenden, da es hauptsächlich für die gesuchten Daten geeignet ist. Tatsächlich kann es jedoch nicht durch eine andere Instanz als die von Ihnen erstellten ersetzt werden. Eigentlich möchten Sie eine eigene Schnittstelle / Klasse erstellen, die Ihr erster Teil erstellt und Ihr zweiter Teil verwendet. Es kann die "echte" LinkedHashMap umschließen und einen Map-Getter bereitstellen oder die Map-Schnittstelle implementieren.
Dies unterscheidet sich ein wenig von der Antwort von CandiedOrange, da ich empfehlen würde, die echte Map zu kapseln (und Aufrufe an sie nach Bedarf zu delegieren), anstatt sie zu erweitern. Es ist manchmal einer dieser heiligen Kriege, aber es klingt für mich sicher, dass es nicht "Eine Karte mit ein paar zusätzlichen Dingen" ist, sondern "Meine Tasche mit nützlichen Zustandsinformationen, die ich intern mit einer Karte repräsentieren kann".
Wenn Sie zwei Variablen hätten, die Sie auf diese Weise weitergeben müssten, hätten Sie wahrscheinlich eine Klasse dafür erstellt, ohne viel darüber nachzudenken. Aber manchmal ist es nützlich, eine Klasse zu haben, auch wenn es sich nur um eine Elementvariable handelt, nur weil es logischerweise dasselbe ist, kein "Wert", sondern "das Ergebnis meiner Operation, mit der ich später etwas anfangen muss".
quelle
MyBagOfUsefulInformation
müßte eine Methode (oder Konstruktor) , um es zu füllen:MyBagOfUsefulInformation.populate(SomeType data)
. Müsstedata
aber das sortierte Abfrageergebnis sein. Was wäreSomeType
, wenn nichtLinkedHashMap
? Ich bin mir nicht sicher, ob ich diesen Fang 22 brechen kann.MyBagOfUsefulInformation
das DAO keine Daten erstellen oder was auch immer erzeugt die Daten in Ihrem System? Warum müssen Sie die zugrunde liegende Karte außerhalb des Herstellers und Verbrauchers der Tasche für den Rest Ihres Codes verfügbar machen?MyBagOfUsefulInformation
als Parameter an die DAO-Methode übergeben habe: softwareengineering.stackexchange.com/a/360079/52573LinkedHashMap ist die einzige Java-Karte, die die von Ihnen gesuchte Funktion für die Reihenfolge der Einfügungen enthält. Das Prinzip der Abhängigkeitsinversion zu verwerfen ist also verlockend und vielleicht sogar praktisch. Überlegen Sie sich jedoch zunächst, was erforderlich ist, um dem zu folgen. Hier ist, was SOLID Sie bitten würde, zu tun.
Anmerkung: Ersetzen Sie den Namen
Ramdal
durch einen beschreibenden Namen, der angibt, dass der Benutzer dieser Schnittstelle der Eigentümer dieser Schnittstelle ist. Das macht es zu der Autorität, die entscheidet, ob die Einfügereihenfolge wichtig ist. Wenn Sie dies nur nennen, habenInsertionOrderMap
Sie den Punkt wirklich verpasst.Ist das ein großes Design vorne? Vielleicht hängt es davon ab, wie wahrscheinlich es ist, dass Sie jemals eine Implementierung benötigen
LinkedHashMap
. Aber wenn Sie DIP nicht nur verfolgen, weil es sehr schmerzhaft wäre, denke ich nicht, dass die Kesselplatte schmerzhafter ist als diese. Dies ist das Muster, das ich verwende, wenn ich möchte, dass unberührbarer Code eine Schnittstelle implementiert, die es nicht tut. Das Schmerzlichste ist wirklich, an gute Namen zu denken.quelle
Vielen Dank für viele gute Anregungen und Denkanstöße.
Am Ende habe ich die Erstellung einer neuen Map-Klasse erweitert und
processData
eine Instanzmethode erstellt:Dann habe ich die DAO-Methode überarbeitet, sodass sie keine Karte zurückgibt, sondern stattdessen eine
target
Karte als Parameter verwendet:Das Auffüllen
DataMap
und Verarbeiten der Daten ist nun ein zweistufiger Prozess, der in Ordnung ist, da es einige andere Variablen gibt, die Teil des Algorithmus sind und von anderen Stellen stammen.Auf diese Weise kann meine Map-Implementierung steuern, wie Einträge in sie eingefügt werden, und die Bestellanforderung wird ausgeblendet - sie ist jetzt ein Implementierungsdetail von
DataMap
.quelle
Wenn Sie mitteilen möchten, dass die von Ihnen verwendete Datenstruktur aus einem bestimmten Grund vorhanden ist, fügen Sie einen Kommentar über der Signatur der Methode ein. Wenn ein anderer Entwickler in Zukunft auf diese Codezeile stößt und eine Toolwarnung bemerkt, bemerkt er möglicherweise auch den Kommentar und "behebt" das Problem nicht mehr. Wenn es keinen Kommentar gibt, hindert sie nichts daran, die Signatur zu ändern.
Das Unterdrücken von Warnungen ist meiner Meinung nach schlechter als das Kommentieren, da die Unterdrückung selbst nicht den Grund angibt, warum die Warnung unterdrückt wurde. Eine Kombination aus Warnungsunterdrückung und Kommentar ist ebenfalls in Ordnung.
quelle
Lassen Sie mich versuchen, Ihren Kontext hier zu verstehen:
Nun, was Sie gerade tun:
Und hier ist dein aktueller Code:
Mein Vorschlag ist, Folgendes zu tun:
Code-Beispiel
Ich vermute, dies würde die Sonar-Warnung beseitigen und auch das für die Verarbeitungsmethode erforderliche signaturspezifische Layout der Daten festlegen.
quelle
MyTupleRepository
Diese Frage ist eigentlich eine Reihe von Problemen mit Ihrem Datenmodell, die in einem zusammengefasst sind. Sie müssen sie nacheinander entwirren. Natürlichere, intuitivere Lösungen fallen heraus, wenn Sie versuchen, jedes Teil des Puzzles zu vereinfachen.
Problem 1: Sie können sich nicht auf DB Order verlassen
Ihre Beschreibungen zum Sortieren Ihrer Daten sind nicht eindeutig.
ORDER BY
Klausel angeben. Wenn dies nicht der Fall ist, weil es zu teuer erscheint, hat Ihr Programm einen Fehler . Datenbanken können Ergebnisse in beliebiger Reihenfolge zurückgeben, wenn Sie keine angeben. Sie können sich nicht darauf verlassen, dass zufällig Daten in der Reihenfolge zurückgegeben werden, nur weil Sie die Abfrage einige Male ausgeführt haben und es so aussieht. Die Reihenfolge kann sich ändern, weil Zeilen auf der Festplatte neu angeordnet werden oder einige gelöscht werden und neue an ihre Stelle treten oder ein Index hinzugefügt wird. Sie müssen eineORDER BY
Klausel angeben . Geschwindigkeit ist wertlos ohne Richtigkeit.ORDER BY
Klausel enthalten sein. Ansonsten hast du Bugs. Wenn eine solche Spalte noch nicht existiert, müssen Sie eine hinzufügen. Typische Optionen für Spalten wie diese wären eine Einfügezeitstempelspalte oder ein automatisch inkrementierender Schlüssel. Der automatische Inkrementierungsschlüssel ist zuverlässiger.Problem 2: Effizientes Sortieren im Speicher
Sobald Sie es sicherstellen , dass garantierte Daten in der Reihenfolge zurückkehren Sie erwarten, können Sie diese Tatsache nutzen , in Erinnerung zu machen Sorten viel effizienter. Fügen Sie der Ergebnismenge Ihrer Abfrage einfach eine
row_number()
oderdense_rank()
-Spalte (oder das Äquivalent Ihrer Datenbank) hinzu. Jetzt hat jede Zeile einen Index , der Ihnen einen direkten Hinweis darauf gibt, wie die Reihenfolge lauten soll, und Sie können trivial danach sortieren. Stellen Sie einfach sicher, dass Sie dem Index einen aussagekräftigen Namen geben (wiesortedBySomethingIndex
).Viola. Jetzt müssen Sie nicht mehr auf die Reihenfolge der Datenbank-Ergebnismenge angewiesen sein.
Problem 3: Müssen Sie diese Verarbeitung überhaupt in Code ausführen?
SQL ist eigentlich sehr mächtig. Es ist eine erstaunliche deklarative Sprache, mit der Sie viele Transformationen und Aggregationen Ihrer Daten durchführen können. Die meisten DBs unterstützen heutzutage sogar zeilenübergreifende Operationen. Sie werden Fenster- oder Analysefunktionen genannt:
OVER
Klausel für FensterfunktionenHaben Sie selbst müssen Ihre Daten in den Speicher so ziehen? Oder können Sie die gesamte Arbeit in der SQL-Abfrage mithilfe von Fensterfunktionen ausführen? Wenn Sie alle (oder nur einen wesentlichen Teil) der Arbeit in der DB erledigen können, fantastisch! Ihr Code-Problem verschwindet (oder wird viel einfacher)!
Problem 4: Was machst du damit
data
?Angenommen, Sie können nicht alles in der DB erledigen, lassen Sie mich das klarstellen. Sie nehmen die Daten als Map (die von Dingen codiert wird, nach denen Sie nicht sortieren möchten), iterieren dann in der Einfügereihenfolge darüber und ändern die Map an Ort und Stelle, indem Sie den Wert einiger Keys ersetzen und hinzufügen neue?
Es tut mir leid, aber was zum Teufel?
Anrufer sollten sich darüber keine Sorgen machen müssen . Das von Ihnen erstellte System ist äußerst instabil. Es braucht nur einen blöden Fehler (vielleicht sogar von Ihnen selbst gemacht, wie wir es alle getan haben), um eine kleine falsche Änderung vorzunehmen, und das Ganze bricht zusammen wie ein Kartenspiel.
Hier ist vielleicht eine bessere Idee:
List
.Eine mögliche Variante könnte darin bestehen, eine sortierte Darstellung zu erstellen und dann eine Zuordnung der Schlüssel zum Index zu erstellen . Auf diese Weise können Sie Ihre sortierte Kopie ändern, ohne versehentlich Duplikate zu erstellen.
Oder vielleicht ist dies sinnvoller: Befreien Sie sich von dem
data
Parameter und lassen SieprocessData
seine eigenen Daten abrufen. Sie können dann dokumentieren, dass Sie dies tun, da es sehr spezielle Anforderungen an die Art und Weise gibt, in der die Daten abgerufen werden. Mit anderen Worten, machen Sie die Funktion zum Eigentümer des gesamten Prozesses, nicht nur eines Teils davon. Die gegenseitigen Abhängigkeiten sind zu stark, um die Logik in kleinere Abschnitte aufzuteilen. (Ändern Sie den Namen der Funktion im Prozess.)Möglicherweise arbeiten diese nicht für Ihre Situation. Ich weiß es nicht ohne vollständige Details des Problems. Aber ich kenne ein fragiles und verwirrendes Design, wenn ich eines höre.
Zusammenfassung
Ich denke, das Problem hier ist letztendlich, dass der Teufel im Detail steckt. Wenn ich auf diese Weise auf Probleme stoße, habe ich normalerweise eine unangemessene Darstellung meiner Daten für das Problem, das ich tatsächlich zu lösen versuche. Die beste Lösung ist , eine bessere Darstellung zu finden , und dann wird mein Problem einfach (vielleicht nicht einfach, aber unkompliziert) zu lösen.
Finden Sie jemanden, der diesen Punkt versteht: Ihre Aufgabe ist es, Ihr Problem auf eine Reihe einfacher, unkomplizierter zu reduzieren. Dann können Sie robusten, intuitiven Code erstellen. Rede mit ihnen. Guter Code und gutes Design lassen Sie denken, dass jeder Idiot sie sich ausgedacht haben könnte, weil sie einfach und unkompliziert sind. Vielleicht gibt es einen erfahrenen Entwickler, mit dem Sie sich austauschen können.
quelle
select key, value from table where ... order by othercolumn
und muss die Reihenfolge bei ihrer Verarbeitung beibehalten. Die Einfügereihenfolge, auf die sie sich beziehen, ist die Einfügereihenfolge in ihrer Karte , definiert durch die in ihrer Abfrage verwendete Reihenfolge, nicht die Einfügereihenfolge in der Datenbank . Dies wird durch die Verwendung von verdeutlichtLinkedHashMap
, einer Datenstruktur, die die Eigenschaften einesMap
und einesList
Schlüssel-Wert-Paares aufweist.order by
Klausel in der Abfrage, aber es ist nicht trivial ( nicht nurorder by column
), so dass ich die Sortierung in Java vermeiden will Neuimplementierung. Obwohl SQL ist mächtig (und wir sprechen von einem Oracle 11g - Datenbank hier), die Art desprocessData
macht Algorithmus einfacher ist es viel in Java auszudrücken. Und ja, "Einfügereihenfolge" bedeutet " Karteneinfügereihenfolge ", dh Abfrageergebnisreihenfolge.