Ich bin relativ neu in Java und finde oft, dass ich eine sortieren muss Map<Key, Value>
den Werten .
Da die Werte nicht eindeutig sind, konvertiere ich die keySet
in eine array
und sortiere dieses Array durch die Arraysortierung mit einem benutzerdefinierten Komparator , der nach dem mit dem Schlüssel verknüpften Wert sortiert.
Gibt es einen einfacheren Weg?
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
undCollections.sort ....
so.Antworten:
Hier ist eine generikfreundliche Version:
quelle
forEachOrdered
stattdessen verwendenforEach
, da in den Dokumenten derforEach
Zustände angegeben ist: "Das Verhalten dieser Operation ist explizit nicht deterministisch."?Wichtige Notiz:
Dieser Code kann auf verschiedene Arten unterbrochen werden. Wenn Sie den bereitgestellten Code verwenden möchten, lesen Sie auch die Kommentare, um sich der Auswirkungen bewusst zu sein. Beispielsweise können Werte nicht mehr mit ihrem Schlüssel abgerufen werden. (
get
kehrt immer zurücknull
.)Es scheint viel einfacher zu sein als alles oben Genannte. Verwenden Sie eine TreeMap wie folgt:
Ausgabe:
quelle
return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))
?map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
Java 8 bietet eine neue Antwort: Konvertieren Sie die Einträge in einen Stream und verwenden Sie die Komparator-Kombinatoren von Map.Entry:
Auf diese Weise können Sie die Einträge in aufsteigender Reihenfolge des Werts sortieren. Wenn Sie einen absteigenden Wert wünschen, kehren Sie einfach den Komparator um:
Wenn die Werte nicht vergleichbar sind, können Sie einen expliziten Komparator übergeben:
Sie können dann andere Stream-Vorgänge verwenden, um die Daten zu verbrauchen. Wenn Sie beispielsweise die Top 10 in einer neuen Karte anzeigen möchten:
Oder drucken Sie an
System.out
:quelle
parallelStream()
in diesem Fall?Drei einzeilige Antworten ...
Ich würde
Google CollectionsGuava verwenden , um dies zu tun - wenn Ihre Werte sindComparable
, können Sie verwendenDadurch wird eine Funktion (ein Objekt) für die Karte erstellt [die eine der Tasten als Eingabe verwendet und den jeweiligen Wert zurückgibt] und dann eine natürliche (vergleichbare) Reihenfolge auf sie [die Werte] angewendet.
Wenn sie nicht vergleichbar sind, müssen Sie etwas in der Art von tun
Diese können nach einer Sortierung auf eine TreeMap (wie
Ordering
erweitertComparator
) oder eine LinkedHashMap angewendet werdenNB : Wenn Sie eine TreeMap verwenden möchten, denken Sie daran, dass bei einem Vergleich == 0 das Element bereits in der Liste enthalten ist (was passiert, wenn Sie mehrere Werte haben, die dasselbe vergleichen). Um dies zu vermeiden, können Sie Ihren Schlüssel wie folgt zum Komparator hinzufügen (vorausgesetzt, Ihre Schlüssel und Werte sind
Comparable
):= Wenden Sie eine natürliche Reihenfolge auf den vom Schlüssel zugeordneten Wert an und kombinieren Sie diese mit der natürlichen Reihenfolge des Schlüssels
Beachten Sie, dass dies immer noch nicht funktionieren , wenn Sie Ihre Schlüssel auf 0 zu vergleichen, aber dies sollte für die meisten ausreichend sein ,
comparable
Gegenstände (wiehashCode
,equals
undcompareTo
sind oft synchron ...)Siehe Ordering.onResultOf () und Functions.forMap () .
Implementierung
Jetzt, da wir einen Komparator haben, der macht, was wir wollen, müssen wir ein Ergebnis daraus ziehen.
Nun wird dies höchstwahrscheinlich funktionieren, aber:
TreeMap
; Es macht keinen Sinn, einen eingefügten Schlüssel zu vergleichen, wenn er erst nach dem Put einen Wert hat, dh er bricht sehr schnellPunkt 1 ist für mich ein Deal-Breaker. Google-Sammlungen sind unglaublich faul (was gut ist: Sie können so ziemlich jeden Vorgang sofort ausführen; die eigentliche Arbeit ist erledigt, wenn Sie das Ergebnis verwenden), und dies erfordert das Kopieren einer ganzen Karte!
"Vollständige" Antwort / Live sortierte Karte nach Werten
Mach dir aber keine Sorgen; Wenn Sie genug davon besessen wären, eine "Live" -Karte auf diese Weise sortieren zu lassen, könnten Sie nicht eines, sondern beide (!) der oben genannten Probleme mit etwas Verrücktem wie dem folgenden lösen:
Hinweis: Dies hat sich im Juni 2012 erheblich geändert - der vorherige Code konnte niemals funktionieren: Zum Nachschlagen der Werte ist eine interne HashMap erforderlich, ohne eine Endlosschleife zwischen
TreeMap.get()
->compare()
undcompare()
-> zu erstellenget()
Wenn wir setzen, stellen wir sicher, dass die Hash-Map den Wert für den Komparator hat, und legen sie dann zum Sortieren in das TreeSet. Zuvor überprüfen wir jedoch die Hash-Map, um festzustellen, dass der Schlüssel kein Duplikat ist. Der von uns erstellte Komparator enthält auch den Schlüssel, damit doppelte Werte die nicht doppelten Schlüssel nicht löschen (aufgrund von == Vergleich). Diese beiden Elemente sind wichtig, um sicherzustellen, dass der Kartenvertrag eingehalten wird. Wenn Sie glauben, dass Sie das nicht wollen, sind Sie fast am Punkt, die Karte vollständig umzukehren
Map<V,K>
.Der Konstruktor müsste als aufgerufen werden
quelle
Ordering
ist einfach ein reicherComparator
. Ich habe versucht, jedes Beispiel zu kommentieren (die Kursivschrift unter jedem Beispiel). "natürlich" zeigt an, dass die Objekte sindComparable
; Es ist wie der ComparableComparator von Apache Common.onResultOf
Wendet eine Funktion auf das zu vergleichende Element an. Wenn Sie also eine Funktion hätten, die einer Ganzzahl 1 hinzufügt,natural().onResultOf(add1Function).compare(1,2)
würde dies am Ende geschehen2.compareTo(3)
ImmutableSetMultiMap
oderImmutableListMultiMap
zu enthalten.Von http://www.programmersheaven.com/download/49349/download.aspx
quelle
Mit Java 8 können Sie die Stream-API verwenden, um dies wesentlich weniger ausführlich zu tun:
quelle
Collections.reverseOrder(comparing(Entry::getValue))
Entry.comparingByValue(Comparator.reverseOrder())
Zum Sortieren der Schlüssel muss der Komparator jeden Wert für jeden Vergleich nachschlagen. Eine skalierbarere Lösung würde das entrySet direkt verwenden, da der Wert dann für jeden Vergleich sofort verfügbar wäre (obwohl ich dies nicht durch Zahlen gesichert habe).
Hier ist eine generische Version von so etwas:
Es gibt Möglichkeiten, die Speicherrotation für die obige Lösung zu verringern. Die erste erstellte ArrayList kann beispielsweise als Rückgabewert wiederverwendet werden. Dies würde die Unterdrückung einiger allgemeiner Warnungen erfordern, aber es könnte sich für wiederverwendbaren Bibliothekscode lohnen. Außerdem muss der Komparator nicht bei jedem Aufruf neu zugewiesen werden.
Hier ist eine effizientere, wenn auch weniger ansprechende Version:
Wenn Sie kontinuierlich auf die sortierten Informationen zugreifen müssen (anstatt sie nur gelegentlich zu sortieren), können Sie eine zusätzliche Multi-Map verwenden. Lassen Sie mich wissen, wenn Sie weitere Details benötigen ...
quelle
Die Commons-Collections-Bibliothek enthält eine Lösung namens TreeBidiMap . Sie können sich auch die Google Collections-API ansehen. Es hat TreeMultimap, die Sie verwenden können.
Und wenn Sie dieses Framework nicht verwenden möchten, werden sie mit Quellcode geliefert.
quelle
Ich habe mir die gegebenen Antworten angesehen, aber viele davon sind komplizierter als nötig oder entfernen Kartenelemente, wenn mehrere Schlüssel den gleichen Wert haben.
Hier ist eine Lösung, die meiner Meinung nach besser passt:
Beachten Sie, dass die Karte vom höchsten zum niedrigsten Wert sortiert ist.
quelle
So erreichen Sie dies mit den neuen Funktionen in Java 8:
Die Einträge werden unter Verwendung des angegebenen Komparators nach ihren Werten geordnet. Wenn Ihre Werte miteinander vergleichbar sind, wird alternativ kein expliziter Komparator benötigt:
Die zurückgegebene Liste ist eine Momentaufnahme der angegebenen Karte zum Zeitpunkt des Aufrufs dieser Methode, sodass beide keine nachfolgenden Änderungen an der anderen widerspiegeln. Für eine iterierbare Live-Ansicht der Karte:
Das zurückgegebene iterable erstellt bei jeder Iteration einen neuen Schnappschuss der angegebenen Karte. Wenn also keine gleichzeitigen Änderungen vorgenommen werden, wird immer der aktuelle Status der Karte angezeigt.
quelle
Erstellen Sie einen benutzerdefinierten Komparator und verwenden Sie ihn beim Erstellen eines neuen TreeMap-Objekts.
Verwenden Sie den folgenden Code in Ihrer Hauptfunktion
Ausgabe:
quelle
Obwohl ich der Meinung bin, dass die ständige Notwendigkeit, eine Karte zu sortieren, wahrscheinlich ein Geruch ist, denke ich, dass der folgende Code der einfachste Weg ist, dies ohne Verwendung einer anderen Datenstruktur zu tun.
}}
Und hier ist ein peinlich unvollständiger Unit-Test:
}}
Das Ergebnis ist eine sortierte Liste von Map.Entry-Objekten, aus denen Sie die Schlüssel und Werte abrufen können.
quelle
Verwenden Sie einen generischen Komparator wie:
quelle
Die Antwort, für die am meisten gestimmt wurde, funktioniert nicht, wenn Sie 2 Elemente haben, die gleich sind. Die TreeMap lässt gleiche Werte aus.
das Beispiel: unsortierte Karte
Ergebnisse
Also lässt E weg !!
Für mich hat es gut funktioniert, den Komparator anzupassen, wenn er gleich ist, wird nicht 0, sondern -1 zurückgegeben.
im Beispiel:
jetzt kehrt es zurück:
unsortierte Karte:
Ergebnisse:
als Antwort auf Aliens (22. November 2011): Ich verwende diese Lösung für eine Karte mit ganzzahligen IDs und Namen, aber die Idee ist dieselbe, daher ist der obige Code möglicherweise nicht korrekt (ich werde ihn in einem Test schreiben und geben Sie den richtigen Code an), dies ist der Code für eine Kartensortierung, basierend auf der obigen Lösung:
und dies ist die Testklasse (ich habe sie gerade getestet, und dies funktioniert für die Ganzzahl, String Map:
Hier ist der Code für den Komparator einer Karte:
und das ist der Testfall dafür:
Natürlich können Sie dies viel allgemeiner machen, aber ich brauchte es nur für 1 Fall (die Karte)
quelle
Anstatt
Collections.sort
wie manche zu verwenden, würde ich vorschlagen, zu verwendenArrays.sort
. EigentlichCollections.sort
macht das so etwas:Es ruft nur
toArray
die Liste auf und verwendet dannArrays.sort
. Auf diese Weise werden alle Karteneinträge dreimal kopiert: einmal von der Karte in die temporäre Liste (sei es eine LinkedList oder ArrayList), dann in das temporäre Array und schließlich in die neue Karte.Meine Lösung verzichtet auf diesen einen Schritt, da keine unnötige LinkedList erstellt wird. Hier ist der Code, generisch freundlich und leistungsoptimal:
quelle
Dies ist eine Variation von Anthonys Antwort, die bei doppelten Werten nicht funktioniert:
Beachten Sie, dass es ziemlich in der Luft liegt, wie man mit Nullen umgeht.
Ein wichtiger Vorteil dieses Ansatzes besteht darin, dass im Gegensatz zu einigen anderen hier angebotenen Lösungen tatsächlich eine Karte zurückgegeben wird.
quelle
Bester Ansatz
Ausgabe
quelle
Hauptproblem. Wenn Sie die erste Antwort verwenden (Google führt Sie hierher), ändern Sie den Komparator, um eine Gleichheitsklausel hinzuzufügen. Andernfalls können Sie keine Werte aus der sortierten Karte nach Schlüsseln abrufen:
quelle
Es gibt bereits viele Antworten auf diese Frage, aber keine hat mir das geliefert, wonach ich gesucht habe, eine Kartenimplementierung, die Schlüssel und Einträge sortiert nach dem zugehörigen Wert zurückgibt und diese Eigenschaft beibehält, wenn Schlüssel und Werte in der Karte geändert werden. Zwei weitere Fragen stellen dies speziell.
Ich habe ein allgemeines freundliches Beispiel erfunden, das diesen Anwendungsfall löst. Diese Implementierung berücksichtigt nicht alle Verträge der Map-Schnittstelle, z. B. die Berücksichtigung von Wertänderungen und Entfernungen in den Mengen, die von keySet () und entrySet () im ursprünglichen Objekt zurückgegeben werden. Ich war der Meinung, dass eine solche Lösung zu groß wäre, um sie in eine Stapelüberlaufantwort aufzunehmen. Wenn es mir gelingt, eine vollständigere Implementierung zu erstellen, werde ich sie möglicherweise an Github senden und dann in einer aktualisierten Version dieser Antwort darauf verlinken.
quelle
Späte Einreise.
Mit dem Aufkommen von Java-8 können wir Streams für die Datenmanipulation auf sehr einfache / prägnante Weise verwenden. Sie können Streams verwenden, um die Karteneinträge nach Wert zu sortieren und eine zu erstellen LinkedHashMap zu Einfügereihenfolge beibehalten wird Iteration.
Z.B:
Bei umgekehrter Reihenfolge ersetzen Sie:
mit
quelle
Entry.comparingByValue()
(als Assylias-Antwort über stackoverflow.com/a/22132422/1480587 ) odercomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
der von Ihnen verwendeten? Ich verstehe, dass Sie auch Schlüssel vergleichen, wenn die Werte identisch sind, oder? Mir ist aufgefallen, dass die Sortierung die Reihenfolge der Elemente mit demselben Wert beibehält. Ist also eine Sortierung nach Schlüsseln erforderlich, wenn die Schlüssel bereits zuvor sortiert wurden?Gegebene Karte
Sortieren Sie die Karte nach dem Wert in aufsteigender Reihenfolge
Sortieren Sie die Karte nach Wert in absteigender Reihenfolge
Ausgabe:
{Software = 50, Technologie = 70, USA = 100, Jobs = 200, Opportunity = 200}
{Jobs = 200, Opportunity = 200, USA = 100, Technologie = 70, Software = 50}
quelle
Abhängig vom Kontext, mit
java.util.LinkedHashMap<T>
welcher Erinnerung die Reihenfolge, in der Elemente in der Karte platziert werden. Andernfalls würde ich empfehlen, eine separate Liste zu führen, über die sortiert werden kann, wenn Sie Werte nach ihrer natürlichen Reihenfolge sortieren müssenCollections.sort()
.quelle
Da TreeMap <> nicht für Werte funktioniert , die gleich sein können, habe ich Folgendes verwendet:
Vielleicht möchten Sie eine Liste in eine LinkedHashMap einfügen , aber wenn Sie sie nur sofort durchlaufen , ist das überflüssig ...
quelle
Das ist einfach zu kompliziert. Karten sollten nicht dazu dienen, sie nach Wert zu sortieren. Am einfachsten ist es, eine eigene Klasse zu erstellen, die Ihren Anforderungen entspricht.
Im unteren Beispiel sollten Sie TreeMap einen Komparator an der Stelle hinzufügen, an der sich * befindet. Mit der Java-API werden jedoch nur Komparatorschlüssel und keine Werte angegeben. Alle hier angegebenen Beispiele basieren auf 2 Karten. Ein Hash und ein neuer Baum. Welches ist seltsam.
Das Beispiel:
Ändern Sie die Karte folgendermaßen in ein Set:
Sie werden Klasse erstellen
Results
,und die Komparatorklasse:
Auf diese Weise können Sie problemlos weitere Abhängigkeiten hinzufügen.
Und als letzten Punkt füge ich einen einfachen Iterator hinzu:
quelle
Basierend auf @ devinmoore-Code, einer Kartensortierungsmethode, die Generika verwendet und sowohl aufsteigende als auch absteigende Reihenfolge unterstützt.
quelle
Hier ist eine OO-Lösung (dh verwendet keine
static
Methoden):Hiermit an die Public Domain gespendet.
quelle
Afaik ist der sauberste Weg, Sammlungen zu verwenden, um die Karte nach Wert zu sortieren:
quelle
Einige einfache Änderungen, um eine sortierte Karte mit Paaren mit doppelten Werten zu erhalten. Geben Sie in der Vergleichsmethode (Klasse ValueComparator) bei gleichen Werten nicht 0 zurück, sondern das Ergebnis des Vergleichs der beiden Schlüssel. Schlüssel sind in einer Karte unterschiedlich, sodass es Ihnen gelingt, doppelte Werte beizubehalten (die übrigens nach Schlüsseln sortiert sind). Das obige Beispiel könnte also folgendermaßen geändert werden:
quelle
Sicher ist die Lösung von Stephen wirklich großartig, aber für diejenigen, die Guava nicht verwenden können:
Hier ist meine Lösung zum Sortieren einer Karte nach Wert. Diese Lösung behandelt den Fall, dass doppelt so viele Werte usw. vorliegen.
Die Exekutive: http://www.ideone.com/dq3Lu
Die Ausgabe:
Hoffe, es wird einigen Leuten helfen
quelle
Wenn Sie doppelte Schlüssel und nur einen kleinen Datensatz (<1000) haben und Ihr Code nicht leistungskritisch ist, können Sie einfach Folgendes tun:
inputUnsortedMap ist die Eingabe in den Code.
Die Variable sortedOutputMap enthält die Daten in absteigender Reihenfolge, wenn sie wiederholt werden. Um die Reihenfolge zu ändern, ändern Sie in der if-Anweisung einfach> in <.
Ist nicht die schnellste Sortierung, erledigt den Job jedoch ohne zusätzliche Abhängigkeiten.
quelle