Jestem stosunkowo nowy w Javie i często stwierdzam, że muszę uporządkować Map<Key, Value>
wartości.
Ponieważ wartości te nie są wyjątkowe, ja znajduję się nawracanie keySet
w produkt array
i sortowania tej tablicy za pośrednictwem tablicy sortowania z niestandardowych komparatora że sortuje na wartości związanej z kluczem.
Czy istnieje prostszy sposób?
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
iCollections.sort ....
tak.Odpowiedzi:
Oto wersja ogólna:
źródło
forEachOrdered
zamiastforEach
, ponieważ docsforEach
stwierdza: „Zachowanie tej operacji jest wyraźnie niedeterministyczny.”?Ważna uwaga:
Ten kod może ulec uszkodzeniu na wiele sposobów. Jeśli zamierzasz użyć dostarczonego kodu, koniecznie przeczytaj komentarze, aby mieć świadomość konsekwencji. Na przykład wartości nie można już pobrać za pomocą klucza. (
get
zawsze wracanull
.)Wydaje się to znacznie łatwiejsze niż wszystkie powyższe. Użyj TreeMap w następujący sposób:
Wynik:
źródło
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 oferuje nową odpowiedź: przekonwertuj wpisy na strumień i użyj kombinacji programów porównawczych z Map.Entry:
Umożliwi to wykorzystanie wpisów posortowanych według rosnącej wartości. Jeśli chcesz wartość malejącą, po prostu odwróć komparator:
Jeśli wartości nie są porównywalne, możesz przekazać jawny komparator:
Następnie możesz przejść do korzystania z innych operacji strumieniowych w celu zużycia danych. Na przykład, jeśli chcesz 10 najlepszych na nowej mapie:
Lub wydrukuj do
System.out
:źródło
parallelStream()
w tym przypadku?Trzy odpowiedzi 1-liniowe ...
W tym celu skorzystałbym z
GoogleGuavaKolekcje- jeśli twoje wartości sąComparable
, możesz użyćKtóry utworzy funkcję (obiekt) dla mapy [która pobiera dowolny z klawiszy jako dane wejściowe, zwracając odpowiednią wartość], a następnie zastosuje do nich naturalne (porównywalne) uporządkowanie [wartości].
Jeśli nie są porównywalne, musisz zrobić coś w stylu
Można je zastosować do TreeMap (w
Ordering
rozszerzeniuComparator
) lub LinkedHashMap po pewnym sortowaniuUwaga : Jeśli zamierzasz użyć TreeMap, pamiętaj, że jeśli porównanie == 0, to element jest już na liście (co się stanie, jeśli masz wiele wartości, które porównują to samo). Aby to złagodzić, możesz dodać swój klucz do komparatora w taki sposób (zakładając, że twoje klucze i wartości są
Comparable
):= Zastosuj naturalne uporządkowanie do wartości odwzorowanej przez klucz i połącz to z naturalnym uporządkowaniem klucza
Zauważ, że to nadal nie zadziała, jeśli twoje klucze będą miały wartość 0, ale powinno wystarczyć dla większości
comparable
przedmiotów (ashashCode
,equals
icompareTo
często są zsynchronizowane ...)Zobacz Ordering.onResultOf () i Functions.forMap () .
Realizacja
Teraz, gdy mamy komparator, który robi to, co chcemy, musimy uzyskać z tego wynik.
Teraz najprawdopodobniej zadziała, ale:
TreeMap
; nie ma sensu porównywać wstawionego klucza, gdy nie ma on wartości aż do momentu wstawienia, tzn. bardzo szybko się psujePunkt 1 jest dla mnie trochę przełomowy; kolekcje google są niezwykle leniwe (co jest dobre: możesz wykonać niemal każdą operację w jednej chwili; prawdziwa praca jest wykonywana, gdy zaczniesz używać wyniku), a to wymaga skopiowania całej mapy!
Odpowiedź „pełna” / mapa posortowana na żywo według wartości
Nie martw się jednak; jeśli miałeś dość obsesji na punkcie posortowanej w ten sposób mapy „na żywo”, możesz rozwiązać nie jeden, ale oba (!) z powyższych problemów, używając czegoś szalonego, takiego jak:
Uwaga: uległo to znacznej zmianie w czerwcu 2012 r. - poprzedni kod nigdy nie mógł działać: wewnętrzny HashMap jest wymagany do wyszukiwania wartości bez tworzenia nieskończonej pętli między
TreeMap.get()
->compare()
acompare()
->get()
Kiedy wstawiamy, upewniamy się, że mapa skrótu ma wartość dla komparatora, a następnie umieszczana w TreeSet w celu sortowania. Ale wcześniej sprawdzamy mapę skrótu, aby zobaczyć, czy klucz nie jest w rzeczywistości duplikatem. Tworzony przez nas komparator będzie również zawierał klucz, aby zduplikowane wartości nie usuwały niepowielonych kluczy (z powodu porównania ==). Te 2 elementy są niezbędne do zachowania kontraktu na mapie; jeśli uważasz, że tego nie chcesz, to prawie jesteś w stanie całkowicie odwrócić mapę (do
Map<V,K>
).Konstruktor musiałby zostać nazwany jako
źródło
Ordering
jest po prostu bogatyComparator
. Próbowałem skomentować każdy przykład (kursywą pod każdym z nich). „naturalny” oznacza, że przedmioty sąComparable
; to jest jak porównywalny komponent Apache common.onResultOf
stosuje funkcję do porównywanego elementu. Więc jeśli miałbyś funkcję, która dodała 1 do liczby całkowitej, tonatural().onResultOf(add1Function).compare(1,2)
by to zrobiła2.compareTo(3)
ImmutableSetMultiMap
lubImmutableListMultiMap
przechowywać kolekcję duplikatów zmiennych.Ze strony http://www.programmersheaven.com/download/49349/download.aspx
źródło
W Javie 8 możesz użyć interfejsu API strumieni, aby zrobić to w znacznie mniej szczegółowy sposób:
źródło
Collections.reverseOrder(comparing(Entry::getValue))
Entry.comparingByValue(Comparator.reverseOrder())
Sortowanie kluczy wymaga, aby Komparator szukał każdej wartości dla każdego porównania. Bardziej skalowalne rozwiązanie użyłoby entrySet bezpośrednio, ponieważ wtedy wartość byłaby natychmiast dostępna dla każdego porównania (chociaż nie poparłem tego liczbowo).
Oto ogólna wersja takiej rzeczy:
Istnieją sposoby zmniejszenia rotacji pamięci dla powyższego rozwiązania. Pierwsza utworzona lista ArrayList może na przykład zostać ponownie użyta jako wartość zwracana; wymagałoby to zniesienia niektórych ogólnych ostrzeżeń, ale może być tego warte w przypadku kodu biblioteki wielokrotnego użytku. Ponadto komparator nie musi być ponownie przydzielany przy każdym wywołaniu.
Oto bardziej wydajna, choć mniej atrakcyjna wersja:
Wreszcie, jeśli chcesz stale uzyskiwać dostęp do posortowanych informacji (zamiast tylko od czasu do czasu sortować je), możesz użyć dodatkowej mapy wielozadaniowej. Daj mi znać, jeśli potrzebujesz więcej szczegółów ...
źródło
Biblioteka commons-collections zawiera rozwiązanie o nazwie TreeBidiMap . Możesz też zapoznać się z interfejsem API Google Maps. Ma TreeMultimap, którego możesz użyć.
A jeśli nie chcesz używać tych ram ... pochodzą one z kodem źródłowym.
źródło
Przejrzałem podane odpowiedzi, ale wiele z nich jest bardziej skomplikowanych niż potrzeba lub usuwam elementy mapy, gdy kilka kluczy ma tę samą wartość.
Oto rozwiązanie, które moim zdaniem pasuje lepiej:
Zauważ, że mapa jest posortowana od najwyższej wartości do najniższej.
źródło
Aby to osiągnąć dzięki nowym funkcjom w Javie 8:
Wpisy są uporządkowane według ich wartości za pomocą danego komparatora. Alternatywnie, jeśli twoje wartości są wzajemnie porównywalne, nie jest potrzebny żaden wyraźny komparator:
Zwrócona lista jest migawką danej mapy w momencie wywołania tej metody, więc żadna z nich nie będzie odzwierciedlała kolejnych zmian w drugiej. Aby zobaczyć iterowalny widok mapy:
Zwracana iterowalność tworzy nową migawkę danej mapy za każdym razem, gdy jest ona iterowana, więc z wyjątkiem jednoczesnej modyfikacji, zawsze będzie odzwierciedlała aktualny stan mapy.
źródło
Utwórz dostosowany komparator i użyj go podczas tworzenia nowego obiektu TreeMap.
Użyj poniższego kodu w swoim głównym func
Wynik:
źródło
Chociaż zgadzam się, że ciągła potrzeba sortowania mapy jest prawdopodobnie zapachem, myślę, że poniższy kod jest najłatwiejszym sposobem na zrobienie tego bez użycia innej struktury danych.
}
A oto zawstydzająco niepełny test jednostkowy:
}
Wynikiem jest posortowana lista obiektów Map.Entry, z których można uzyskać klucze i wartości.
źródło
Użyj ogólnego komparatora, takiego jak:
źródło
Odpowiedź najbardziej głosowana nie działa, jeśli masz 2 równe przedmioty. TreeMap pozostawia równe wartości.
exmaple: nieposortowana mapa
wyniki
Więc pomija E !!
Dla mnie dobrze działało dostosowanie komparatora, jeśli jest równe, nie zwraca 0, ale -1.
w przykładzie:
teraz zwraca:
nieposortowana mapa:
wyniki:
w odpowiedzi na Aliens (22 listopada 2011 r.): Używam tego rozwiązania do mapy identyfikatorów i nazw liczb całkowitych, ale pomysł jest taki sam, więc powyższy kod może być nieprawidłowy (napiszę go w teście i podamy prawidłowy kod), jest to kod do sortowania map w oparciu o powyższe rozwiązanie:
i to jest klasa testowa (właśnie ją przetestowałem i działa to na Integer, String Map:
oto kod dla komparatora mapy:
i to jest przykład tego:
oczywiście możesz uczynić to o wiele bardziej ogólnym, ale potrzebowałem go tylko na 1 skrzynkę (Mapa)
źródło
Zamiast używać
Collections.sort
jak niektórzy sugeruję użycieArrays.sort
. W rzeczywistościCollections.sort
działa to tak:Po prostu wywołuje
toArray
listę, a następnie używaArrays.sort
. W ten sposób wszystkie wpisy na mapie zostaną skopiowane trzykrotnie: raz z mapy na listę tymczasową (może to być LinkedList lub ArrayList), następnie do tablicy tymczasowej i na koniec do nowej mapy.Moje rozwiązanie pomija ten jeden krok, ponieważ nie tworzy niepotrzebnej listy LinkedList. Oto kod, ogólny i optymalny pod względem wydajności:
źródło
Jest to odmiana odpowiedzi Anthony'ego, która nie działa, jeśli istnieją zduplikowane wartości:
Zauważ, że radzenie sobie z zerami jest raczej w powietrzu.
Jedną ważną zaletą tego podejścia jest to, że faktycznie zwraca mapę, w przeciwieństwie do niektórych innych rozwiązań tutaj oferowanych.
źródło
Najlepsze podejście
Wynik
źródło
Główny problem. Jeśli użyjesz pierwszej odpowiedzi (Google zabierze Cię tutaj), zmień komparator, aby dodać klauzulę równości, w przeciwnym razie nie możesz uzyskać wartości z sorted_map według kluczy:
źródło
Istnieje wiele odpowiedzi na to pytanie, ale żadna nie dostarczyła mi tego, czego szukałem, implementacji mapy, która zwraca klucze i wpisy posortowane według powiązanej wartości, i zachowuje tę właściwość, gdy klucze i wartości są modyfikowane na mapie. Zadają to dwa inne pytania .
Przygotowałem ogólny, przyjazny przykład, który rozwiązuje ten przypadek użycia. Ta implementacja nie uwzględnia wszystkich umów interfejsu mapy, takich jak odzwierciedlenie zmian wartości i usuwania w zestawach zwracanych z keySet () i entrySet () w oryginalnym obiekcie. Czułem, że takie rozwiązanie byłoby zbyt duże, aby można je było uwzględnić w odpowiedzi na przepełnienie stosu. Jeśli uda mi się stworzyć pełniejszą implementację, być może opublikuję ją w Github, a następnie w linku w zaktualizowanej wersji tej odpowiedzi.
źródło
Późne wejście
Wraz z pojawieniem się Java-8 możemy używać strumieni do manipulacji danymi w bardzo łatwy / zwięzły sposób. Możesz użyć strumieni, aby posortować wpisy mapy według wartości i utworzyć LinkedHashMap, która zachowa iterację kolejności wstawiania .
Na przykład:
W przypadku odwrotnego zamówienia wymień:
z
źródło
Entry.comparingByValue()
(jako odpowiedzi assyli powyżej stackoverflow.com/a/22132422/1480587 ) lubcomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
której użyłeś? Rozumiem, że porównujesz klucze, jeśli wartości są identyczne, prawda? Zauważyłem, że sortowanie utrzymuje porządek elementów o tej samej wartości - czy więc sortowanie według kluczy jest konieczne, jeśli klucze były już wcześniej sortowane?Podana mapa
Posortuj mapę według wartości w porządku rosnącym
Posortuj mapę według wartości w porządku malejącym
Wynik:
{oprogramowanie = 50, technologia = 70, USA = 100, miejsca pracy = 200, okazja = 200}
{miejsca pracy = 200, szansa = 200, USA = 100, technologia = 70, oprogramowanie = 50}
źródło
W zależności od kontekstu, przy użyciu
java.util.LinkedHashMap<T>
którego pamięta się kolejność, w jakiej przedmioty są umieszczane na mapie. W przeciwnym razie, jeśli trzeba posortować wartości na podstawie ich naturalnego uporządkowania, zaleciłbym utrzymanie osobnej listy, którą można sortowaćCollections.sort()
.źródło
Ponieważ TreeMap <> nie działa dla wartości, które mogą być równe, użyłem tego:
Możesz chcieć umieścić listę w LinkedHashMap , ale jeśli masz zamiar iterować od razu, to jest zbyteczne ...
źródło
To jest zbyt skomplikowane. Mapy nie powinny wykonywać takich zadań, jak sortowanie ich według wartości. Najłatwiejszym sposobem jest stworzenie własnej klasy, która spełni twoje wymagania.
Na przykład niżej powinieneś dodać TreeMap komparator w miejscu, gdzie * jest. Ale przez java API daje komparatorowi tylko klucze, a nie wartości. Wszystkie podane tutaj przykłady oparte są na 2 mapach. Jeden hasz i jedno nowe drzewo. Co jest dziwne.
Przykład:
Zmień mapę na zestaw w ten sposób:
Stworzysz klasę
Results
,oraz klasa komparatora:
W ten sposób możesz łatwo dodać więcej zależności.
I jako ostatni punkt dodam prosty iterator:
źródło
Oparty na kodzie @devinmoore, metody sortowania map przy użyciu ogólnych i obsługujących zarówno rosnącą, jak i malejącą kolejność.
źródło
Oto rozwiązanie OO (tzn. Nie używa
static
metod):Niniejszym przekazaliśmy na rzecz domeny publicznej.
źródło
Afaik najczystszym sposobem jest wykorzystanie kolekcji do sortowania mapy według wartości:
źródło
Kilka prostych zmian w celu uzyskania posortowanej mapy z parami, które mają zduplikowane wartości. W metodzie porównania (klasa ValueComparator), gdy wartości są równe, nie zwraca 0, ale zwraca wynik porównania 2 kluczy. Klucze są różne na mapie, więc udaje się zachować duplikaty wartości (które są przy okazji sortowane według kluczy). Powyższy przykład można zmodyfikować w następujący sposób:
źródło
Na pewno rozwiązanie Stephena jest naprawdę świetne, ale dla tych, którzy nie mogą używać Guawy:
Oto moje rozwiązanie do sortowania według wartości mapy. To rozwiązanie obsługuje przypadek, w którym występuje dwukrotnie ta sama wartość itp.
Wykonanie: http://www.ideone.com/dq3Lu
Wyjście:
Mam nadzieję, że pomoże to niektórym ludziom
źródło
Jeśli masz zduplikowane klucze i tylko niewielki zestaw danych (<1000), a Twój kod nie ma krytycznego wpływu na wydajność, możesz wykonać następujące czynności:
inputUnsortedMap to dane wejściowe do kodu.
Zmienna sortedOutputMap będzie zawierać dane w porządku malejącym po iteracji. Aby zmienić kolejność, po prostu zmień> na <w instrukcji if.
Nie jest najszybszym sortowaniem, ale wykonuje pracę bez żadnych dodatkowych zależności.
źródło