Mapa<String, Integer> - sortowanie pierw względem wartości, a jeśli wartości równe to klucze alfabetycznie

0

Cześć

Z góry ostrzegam, że jestem początkujący i będę bardzo wdzięczny za wyrozumiałość :) Mam problem w zadaniu. W kodzie potrzebuję posortować mapę<String, Integer> pierw względem wartości (od największej liczby do najmniejszej), a jeśli liczby są równe to, aby ułożenie String'ów, które mają równe wartości, było alfabetycznie. I tu pojawia się klops, bo szukałem na internecie i znalazłem w jednym miejscu, ale wpis miał miejsce 11 lat temu, jest jedno rozwiązanie i wygląda bardzo na przekombinowane.
Póki co jedynie posortowałem względem wartości:

private static Map<String, Integer> returnSortedMap(Map<String, Integer> map) {
        List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
        list.sort(Map.Entry.comparingByValue());
        Collections.reverse(list);

        Map<String, Integer> sortedMap = new LinkedHashMap<>();
        for (Map.Entry<String, Integer> entry : list) {
            sortedMap.put(entry.getKey(), entry.getValue());
        }
        return sortedMap;

Jak to zmodyfikować, aby sortowało tak jak napisałem? Żeby to było ładnie i schludnie i nie przesadzać z ilością linii.

Będę bardzo wdzięczny za pomoc, szczególnie, że to bardzo ważne zadanie! :)

3
Comparator<Map.Entry<String, Integer>> customComparator =
                Map.Entry.<String, Integer>comparingByValue(Comparator.reverseOrder())
                        .thenComparing(Map.Entry.comparingByKey());

        Map<String, Integer> result = map.entrySet()
                .stream()
                .sorted(customComparator)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
0

@kixe52: Dzięki wielkie za pomoc! Użyłem innego, ze względu na to, że nie uczyłem się części funkcji z tych co użyłeś to średnio rozumiem co tam się dzieje, a jestem na etapie nauki dosyć na początku :)
Dla chętnych wrzucam to co ja znalazłem gdzieś głęboko w stack'u:

private static Map<String, Integer> returnSortedMap(Map<String, Integer> map) {
        List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
                // Check if values are the same
                if (e1.getValue().equals(e2.getValue()))
                    // Compare e1 to e2, because A should be first element
                    return e1.getKey().compareTo(e2.getKey());
                else
                    // Compare e2 to e1, because largest number should be first
                    return e2.getValue().compareTo(e1.getValue());
            }
        });

        Map<String, Integer> result = new LinkedHashMap<>();
        for (Map.Entry<String, Integer> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }

3

Rozwiązania są bardzo podobne - ja robię na streamach, ty iterujesz 'ręcznie'.
Mówiąc, że nie rozumiesz mojego przypuszczam, że 'swojego' też nie rozumiesz. W końcu skopiowałeś je z internetu.
Nauka polega również na tym aby analizować i starać się zrozumieć rozwiązania znalezione w internecie.

3

Rozwiązanie @kixe52 jest dużo lepsze - nie tworzy dodatkowej listy i co by tu nie mówić - jest czytelniejsze.

Jedyne zastrzeżenie jakie mam to zamieniłbym:
Map.Entry.<String, Integer> comparingByValue(Comparator.reverseOrder()) na Map.Entry.<String, Integer> comparingByValue().reversed() - IMO czytelniej, ale pod spodem to jest to samo.

0

@kixe52: nie miałem na kursie jeszcze streamów, dlatego nie rozumiem Twojego. A Twoje używa jeszcze collect i comparatora. "Moje" tylko i wyłącznie comparator, który też dopiero będę miał na kursie, ale rozumiem, że jest to porównanie i poprzez Override nadpisujemy tą metodę. Więc tylko i wyłącznie jest kwestia tego, dlaczego "moje" bardziej rozumiem. Ale na prawdę jestem mega wdzięczny, bo jak ruszę stream'y, a będzie to niedługo to będę wiedział co i jak przy tym porównywaniu :)

0

Słabości w tym kodzie jest więcej, i koledzy ze streamami też nie dotykają istoty problemu.

Po pierwsze sortujesz zupę łyżką (prawidłowo, bo nie widelcem), by ją wlać do durszlaka.

LinkedHashMap to takie dwa-w-jednym, List i HashMap
O ile trzyma w jakiejkolwiek stabilnej kolejności , to tylko jako lista (w kolejności dodawania).
Jako HashMap kolejność jest (w ludzkim sensie) przypadkowa.
Masz pierwszą naukę: wszystko co ma w nazwie Hash, nie ma naturalnej kolejności.

Więc jest to bezsensowny Map na "posortowane" dane, ktoś to użyje jako Map (a nie listę) dostanie kolejność przypadkową.

Kontenerem z grupy 'Map' który nie tylko trzyma sortowanie, ale wręcz je wymusza jest TreeMap - co czyni sortowanie zbędne, wystarczy po kolei podstawić z wejściowego Mapa jaki by nie był, do TreeMapa (podając komparator - masz tu opisane)
https://www.baeldung.com/java-treemap-vs-hashmap

1

@ZrobieDobrze:

Więc jest to bezsensowny Map na "posortowane" dane, ktoś to użyje jako Map (a nie listę) dostanie kolejność przypadkową.

Mylisz się i to bardzo.

  1. Nie można użyć mapy jako listy.
  2. Posortowana kolekcja to posortowana kolekcja. Nie ma znaczenia jaką strukturę masz pod spodem.
  3. TreeMap do tego przypadku kompletnie się nie nadaje z prostej przyczyny - sortuje po kluczach. Kolega próbuje posortować najpierw po wartościach, a potem po kluczach - czyli klucze mogą lecieć w kolejności 1,3,2,5,4 i to będzie poprawnie.
0

@wartek01:
Prawda, trzeba by przepakować do nowego klucza.

Ty niemniej LinkedHashMap to kiszka z mocy ustawy.

1

@ZrobieDobrze: nie. W tym konkretnym przypadku TreeMap jest bardzo nienaturalne dla tego problemu, natomiast LinkedHashMap tutaj świetnie pasuje.

0
wartek01 napisał(a):

@ZrobieDobrze: nie. W tym konkretnym przypadku TreeMap jest bardzo nienaturalne dla tego problemu, natomiast LinkedHashMap tutaj świetnie pasuje.

Równie dobrze List, da tyle samo (przeliterowanie, ale nie wyszukiwanie)

Edit: Faktem jest, że w bibliotece standardowej nie ma nic oprócz obu jakie podnosimy. Zapamiętam sobie, jaki ten mój Comparator jest, jednak inny od moich mglistych wspomnień ;)

0

@ZrobieDobrze: lista się nie nada bo chcesz mieć na wyjściu mapę , nie listę.

Mam wrażenie , że mylisz dwie kwestie - porządek naturalny i sortowanie, i usilnie próbujesz rozwiązać problem sortowania poprzez wrzucenie danych do struktury co będzie trzymmać porządek zawsze. To błąd, bo w całej informatyce pelno jest sytuacji w której masz jakąś posortowaną strukturę, ale nie są one w jakimś porządku naturalnym. Np. https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array/11227902

0

@wartek01:

Do q nędzy, czy ja napisałem taki typ zwracany?

private static Map<String, Integer> returnSortedMap(..

Dla każdego kto czyta deklaracje, intencje są czytelne, jeśli ktoś zwraca Map to po to, aby klient używał tego jak Map, dla mnie to proste - może za mało kawy wypiłem, ale nie sądzę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1