it-swarm-eu.dev

Seřadit mapu <klíč, hodnota> podle hodnot

Jsem poměrně nový Java a často zjistím, že potřebuji třídit Map<Key, Value> na hodnotách.

Vzhledem k tomu, že hodnoty nejsou jedinečné, zjistím, že převedu keySet do array a třídění, které pole přes array sort s vlastní komparátor , které třídí podle hodnoty spojené s klíčem.

Existuje snadnější způsob?

1473
Abe

Zde je obecná verze:

public class MapUtil {
    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        List<Entry<K, V>> list = new ArrayList<>(map.entrySet());
        list.sort(Entry.comparingByValue());

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

        return result;
    }
}
824
Carter Page

Důležitá poznámka:

Tento kód se může rozdělit několika způsoby. Pokud máte v úmyslu použít poskytnutý kód, přečtěte si také poznámky, abyste si byli vědomi důsledků. Například hodnoty již nelze načíst klíčem. (get vždy vrátí null.)


Zdá se, že je mnohem snazší než všechny výše uvedené. Použijte TreeMap takto:

public class Testing {
    public static void main(String[] args) {
        HashMap<String, Double> map = new HashMap<String, Double>();
        ValueComparator bvc = new ValueComparator(map);
        TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);

        map.put("A", 99.5);
        map.put("B", 67.4);
        map.put("C", 67.4);
        map.put("D", 67.3);

        System.out.println("unsorted map: " + map);
        sorted_map.putAll(map);
        System.out.println("results: " + sorted_map);
    }
}

class ValueComparator implements Comparator<String> {
    Map<String, Double> base;

    public ValueComparator(Map<String, Double> base) {
        this.base = base;
    }

    // Note: this comparator imposes orderings that are inconsistent with
    // equals.
    public int compare(String a, String b) {
        if (base.get(a) >= base.get(b)) {
            return -1;
        } else {
            return 1;
        } // returning 0 would merge keys
    }
}

Výstup:

unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
results: {D=67.3, B=67.4, C=67.4, A=99.5}
408
user157196

Java 8 nabízí novou odpověď: převést položky do streamu a použít komparátorové komparátory z Map.Entry:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue());

To vám umožní konzumovat položky seřazené vzestupně podle hodnoty. Pokud chcete sestupnou hodnotu, jednoduše změňte komparátor:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));

Pokud hodnoty nejsou srovnatelné, můžete předat explicitní komparátor:

Stream<Map.Entry<K,V>> sorted =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(comparator));

Poté můžete pokračovat v používání jiných operací toku dat, abyste mohli data spotřebovat. Chcete-li například 10 nejlepších v nové mapě:

Map<K,V> topTen =
    map.entrySet().stream()
       .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
       .limit(10)
       .collect(Collectors.toMap(
          Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

Nebo tisknout na System.out:

map.entrySet().stream()
   .sorted(Map.Entry.comparingByValue())
   .forEach(System.out::println);
254
Brian Goetz

Tři 1-řádkové odpovědi ...

Použil bych Sbírky Google Guava k tomu - pokud jsou vaše hodnoty Comparable, pak můžete použít

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))

Která vytvoří funkci (objekt) pro mapu [která vezme některý z klíčů jako vstup, vrací příslušnou hodnotu] a pak na ně použije přirozené (srovnatelné) uspořádání [hodnoty].

Pokud nejsou srovnatelné, pak budete muset udělat něco v duchu

valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map)) 

Ty mohou být aplikovány na TreeMap (jako Ordering prodloužení Comparator), nebo LinkedHashMap po nějakém třídění

NB: Pokud chcete použít TreeMap, nezapomeňte, že pokud je srovnání = 0, pak je položka již v seznamu (což se stane, pokud máte více hodnot, které srovnávají stejné). . Chcete-li to zmírnit, můžete přidat klíč ke komparátoru, jako je tento (předpokládá se, že klíče a hodnoty jsou Comparable):

valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())

= Použít přirozené uspořádání na hodnotu namapovanou klíčem a složit, že s přirozeným uspořádáním klíče

Všimněte si, že to nebude fungovat, pokud vaše klíče budou porovnávány s hodnotou 0, ale to by mělo být dostačující pro většinu comparable položek (jako hashCode, equals a compareTo jsou často synchronizovány ...)

Viz Ordering.onResultOf () a Functions.forMap () .

Implementace

Takže teď, když máme komparátor, který dělá to, co chceme, musíme z toho dostat výsledek. 

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

Toto bude s největší pravděpodobností pracovat, ale:

  1. je třeba provést kompletní hotovou mapu
  2. Nezkoušejte komparátory výše na TreeMap; nemá smysl se pokoušet porovnat vložený klíč, když nemá hodnotu až po vložení, to znamená, že se zlomí opravdu rychle

Bod 1 je pro mě trochu rozdělovačem; Google sbírky je neuvěřitelně líný (což je dobré: můžete udělat téměř všechny operace v okamžiku, skutečná práce se provádí, když začnete používat výsledek), a to vyžaduje kopírování celé mapu!

"Plná" odpověď/Živá tříděná mapa podle hodnot

Nebojte se; pokud jste byli dostatečně posedlí tím, že máte takto uspořádanou "živou" mapu, můžete řešit ne jeden, ale oba (!) výše uvedených problémů s něčím bláznivým, jako je následující:

Poznámka: To se výrazně změnilo v červnu 2012 - předchozí kód nemohl nikdy fungovat: interní HashMap je vyžadován pro vyhledávání hodnot bez vytvoření nekonečné smyčky mezi TreeMap.get() -> compare() a compare() -> get()

import static org.junit.Assert.assertEquals;

import Java.util.HashMap;
import Java.util.Map;
import Java.util.TreeMap;

import com.google.common.base.Functions;
import com.google.common.collect.Ordering;

class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
    //A map for doing lookups on the keys for comparison so we don't get infinite loops
    private final Map<K, V> valueMap;

    ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
        this(partialValueOrdering, new HashMap<K,V>());
    }

    private ValueComparableMap(Ordering<? super V> partialValueOrdering,
            HashMap<K, V> valueMap) {
        super(partialValueOrdering //Apply the value ordering
                .onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
                .compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
        this.valueMap = valueMap;
    }

    public V put(K k, V v) {
        if (valueMap.containsKey(k)){
            //remove the key in the sorted set before adding the key again
            remove(k);
        }
        valueMap.put(k,v); //To get "real" unsorted values for the comparator
        return super.put(k, v); //Put it in value order
    }

    public static void main(String[] args){
        TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
        map.put("a", 5);
        map.put("b", 1);
        map.put("c", 3);
        assertEquals("b",map.firstKey());
        assertEquals("a",map.lastKey());
        map.put("d",0);
        assertEquals("d",map.firstKey());
        //ensure it's still a map (by overwriting a key, but with a new value) 
        map.put("d", 2);
        assertEquals("b", map.firstKey());
        //Ensure multiple values do not clobber keys
        map.put("e", 2);
        assertEquals(5, map.size());
        assertEquals(2, (int) map.get("e"));
        assertEquals(2, (int) map.get("d"));
    }
 }

Když dáme, zajistíme, že má hašovací mapa hodnotu pro komparátor a pak se umístí do TreeSetu pro třídění. Ale předtím, než zkontrolujeme hash mapu vidět, že klíč není ve skutečnosti duplikát. Komparátor, který vytvoříme, bude také obsahovat klíč, aby duplicitní hodnoty neodstraňovaly duplicitní klíče (kvůli porovnání ==). Tyto 2 položky jsou vitální pro zajištění uchovávání mapové smlouvy; pokud si myslíte, že to nechcete, pak jste téměř v bodě, kdy mapu zcela převrátíte (na Map<V,K>).

Konstruktor by musel být volán jako 

 new ValueComparableMap(Ordering.natural());
 //or
 new ValueComparableMap(Ordering.from(comparator));
208
Stephen

Z http://www.programmersheaven.com/download/49349/download.aspx

private static <K, V> Map<K, V> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> list = new LinkedList<>(map.entrySet());
    Collections.sort(list, new Comparator<Object>() {
        @SuppressWarnings("unchecked")
        public int compare(Object o1, Object o2) {
            return ((Comparable<V>) ((Map.Entry<K, V>) (o1)).getValue()).compareTo(((Map.Entry<K, V>) (o2)).getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<>();
    for (Iterator<Entry<K, V>> it = list.iterator(); it.hasNext();) {
        Map.Entry<K, V> entry = (Map.Entry<K, V>) it.next();
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}
180
devinmoore

S Java 8 můžete použít streams api pro to, abyste to provedli podstatně méně podrobným způsobem:

Map<K, V> sortedMap = map.entrySet().stream()
                         .sorted(Entry.comparingByValue())
                         .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
44
assylias

Třídění klíčů vyžaduje, aby komparátor vyhledal každou hodnotu pro každé porovnání. Škálovatelnější řešení by použilo položku entrySet přímo, protože hodnota by byla okamžitě k dispozici pro každé porovnání (i když jsem to nezaložila čísly).

Zde je obecná verze takové věci:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue(Map<K, V> map) {
    final int size = map.size();
    final List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(size);
    list.addAll(map.entrySet());
    final ValueComparator<V> cmp = new ValueComparator<V>();
    Collections.sort(list, cmp);
    final List<K> keys = new ArrayList<K>(size);
    for (int i = 0; i < size; i++) {
        keys.set(i, list.get(i).getKey());
    }
    return keys;
}

private static final class ValueComparator<V extends Comparable<? super V>>
                                     implements Comparator<Map.Entry<?, V>> {
    public int compare(Map.Entry<?, V> o1, Map.Entry<?, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

Existují způsoby, jak snížit rotaci paměti pro výše uvedené řešení. První vytvořený ArrayList by mohl být například znovu použit jako návratová hodnota; to by vyžadovalo potlačení některých generických varování, ale za to by mohlo být vhodné použít znovu použitelný kód knihovny. Také komparátor nemusí být při každém vyvolání znovu přidělen.

Zde je efektivnější, i když méně přitažlivá verze:

public static <K, V extends Comparable<? super V>> List<K> getKeysSortedByValue2(Map<K, V> map) {
    final int size = map.size();
    final List reusedList = new ArrayList(size);
    final List<Map.Entry<K, V>> meView = reusedList;
    meView.addAll(map.entrySet());
    Collections.sort(meView, SINGLE);
    final List<K> keyView = reusedList;
    for (int i = 0; i < size; i++) {
        keyView.set(i, meView.get(i).getKey());
    }
    return keyView;
}

private static final Comparator SINGLE = new ValueComparator();

Nakonec, pokud potřebujete nepřetržitý přístup k utříděným informacím (a nikoliv jen jednou za čas), můžete použít další multi mapu. Dejte nám vědět, pokud potřebujete více informací ...

30
volley

Knihovna commons-collection obsahuje řešení s názvem TreeBidiMap . Nebo se můžete podívat na API služby Google Collections. Má TreeMultimap které můžete použít.

A pokud nechcete používat tyto rámce ... přicházejí se zdrojovým kódem.

25
p3t0r

Podíval jsem se na dané odpovědi, ale mnoho z nich je složitější, než je potřeba, nebo odstraňovat prvky mapy, když má několik klíčů stejnou hodnotu.

Zde je řešení, které je podle mého názoru lepší:

public static <K, V extends Comparable<V>> Map<K, V> sortByValues(final Map<K, V> map) {
    Comparator<K> valueComparator =  new Comparator<K>() {
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0) return 1;
            else return compare;
        }
    };
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Všimněte si, že mapa je rozdělena z nejvyšší hodnoty na nejnižší.

24
Anthony

Toho dosáhnete s novými funkcemi v jazyce Java 8:

import static Java.util.Map.Entry.comparingByValue;
import static Java.util.stream.Collectors.toList;

<K, V> List<Entry<K, V>> sort(Map<K, V> map, Comparator<? super V> comparator) {
    return map.entrySet().stream().sorted(comparingByValue(comparator)).collect(toList());
}

Položky jsou seřazeny podle hodnot pomocí daného komparátoru. Alternativně, pokud jsou vaše hodnoty vzájemně srovnatelné, není nutný žádný explicitní komparátor:

<K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map) {
    return map.entrySet().stream().sorted(comparingByValue()).collect(toList());
}

Vrácený seznam je snímek dané mapy v době, kdy je tato metoda volána, takže ani další neodrážejí další změny. Pro živý iterovatelný pohled na mapu:

<K, V extends Comparable<? super V>> Iterable<Entry<K, V>> sort(Map<K, V> map) {
    return () -> map.entrySet().stream().sorted(comparingByValue()).iterator();
}

Vrácený iterable vytvoří nový snímek dané mapy pokaždé, když je iterován, takže blokování souběžné modifikace bude vždy odrážet aktuální stav mapy.

17
gdejohn

I když souhlasím s tím, že neustálá potřeba třídit mapu je pravděpodobně pach, myslím, že následující kód je nejjednodušší způsob, jak to udělat bez použití jiné datové struktury.

public class MapUtilities {

public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
    Collections.sort(entries, new ByValue<K, V>());
    return entries;
}

private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
    public int compare(Entry<K, V> o1, Entry<K, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

}

A tady je rozpačitě neúplný test jednotky:

public class MapUtilitiesTest extends TestCase {
public void testSorting() {
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("One", 1);
    map.put("Two", 2);
    map.put("Three", 3);

    List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
    assertEquals("First", "One", sorted.get(0).getKey());
    assertEquals("Second", "Two", sorted.get(1).getKey());
    assertEquals("Third", "Three", sorted.get(2).getKey());
}

}

Výsledkem je tříděný seznam objektů Map.Entry, ze kterých můžete získat klíče a hodnoty.

14
Lyudmil

Vytvořte přizpůsobený komparátor a použijte jej při vytváření nového objektu TreeMap.

class MyComparator implements Comparator<Object> {

    Map<String, Integer> map;

    public MyComparator(Map<String, Integer> map) {
        this.map = map;
    }

    public int compare(Object o1, Object o2) {

        if (map.get(o2) == map.get(o1))
            return 1;
        else
            return ((Integer) map.get(o2)).compareTo((Integer)     
                                                            map.get(o1));

    }
}

Použijte níže uvedený kód ve vaší hlavní func

    Map<String, Integer> lMap = new HashMap<String, Integer>();
    lMap.put("A", 35);
    lMap.put("B", 75);
    lMap.put("C", 50);
    lMap.put("D", 50);

    MyComparator comparator = new MyComparator(lMap);

    Map<String, Integer> newMap = new TreeMap<String, Integer>(comparator);
    newMap.putAll(lMap);
    System.out.println(newMap);

Výstup:

{B=75, D=50, C=50, A=35}
14
Sujan Reddy A

Použijte obecný komparátor, například:

final class MapValueComparator<K,V extends Comparable<V>> implements Comparator<K> {

    private Map<K,V> map;

    private MapValueComparator() {
        super();
    }

    public MapValueComparator(Map<K,V> map) {
        this();
        this.map = map;
    }

    public int compare(K o1, K o2) {
        return map.get(o1).compareTo(map.get(o2));
    }
}
11
RoyalBigorno

Odpověď hlasovala pro většinu nefunguje, když máte 2 položky, které se rovná. TreeMap ponechává stejné hodnoty ven.

exmaple: netříděná mapa

 klíč/hodnota: D/67.3 
 klíč/hodnota: A/99.5 
 klíč/hodnota: B/67.4 
 klíč/hodnota: C/67.5 
 klíč/hodnota: E/99.5 

výsledek

 klíč/hodnota: A/99.5 
/hodnota: C/67.5 
 klíč/hodnota: B/67,4 
 klíč/hodnota: D/67.3 

Takže opouští E!

Pro mě to fungovalo dobře, abych upravil komparátor, pokud se rovná nule 0, ale -1.

v příkladu:

třída ValueComparator implementuje komparátor {

Mapová základna, Public ValueComparator (Mapová báze) {[.____. This.base = base; }

public int compare (Objekt a, objekt b) {

if((Double)base.get(a) < (Double)base.get(b)) {
  return 1;
} else if((Double)base.get(a) == (Double)base.get(b)) {
  return -1;
} else {
  return -1;
}

} }

nyní se vrátí:

netříděná mapa:

 klíč/hodnota: D/67.3 
 klíč/hodnota: A/99.5 
 klíč/hodnota: B/67.4 
 klíč/hodnota: C/67.5 
 klíč/hodnota: E/99.5 

výsledek:

 klíč/hodnota: A/99.5 
 klíč/hodnota: E/99.5 
 klíč/hodnota: C/67.5 
 klíč/hodnota: B/67.4 
 klíč/hodnota: D/67.3 

jako odpověď na Cizince (2011 nov. 22): Používám toto řešení pro mapu ID a názvů integerů, ale myšlenka je stejná, takže může být kód výše není správný (budu napište to do testu a dejte vám správný kód), jedná se o kód pro třídění map, založený na výše uvedeném řešení:

package nl.iamit.util;

import Java.util.Comparator;
import Java.util.Map;

public class Comparators {


    public static class MapIntegerStringComparator implements Comparator {

        Map<Integer, String> base;

        public MapIntegerStringComparator(Map<Integer, String> base) {
            this.base = base;
        }

        public int compare(Object a, Object b) {

            int compare = ((String) base.get(a))
                    .compareTo((String) base.get(b));
            if (compare == 0) {
                return -1;
            }
            return compare;
        }
    }


}

a to je testovací třída (jen jsem to otestovala, a to funguje pro Integer, String Map:

package test.nl.iamit.util;

import Java.util.HashMap;
import Java.util.TreeMap;
import nl.iamit.util.Comparators;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;

public class TestComparators {


    @Test
    public void testMapIntegerStringComparator(){
        HashMap<Integer, String> unSoretedMap = new HashMap<Integer, String>();
        Comparators.MapIntegerStringComparator bvc = new Comparators.MapIntegerStringComparator(
                unSoretedMap);
        TreeMap<Integer, String> sorted_map = new TreeMap<Integer, String>(bvc);
        //the testdata:
        unSoretedMap.put(new Integer(1), "E");
        unSoretedMap.put(new Integer(2), "A");
        unSoretedMap.put(new Integer(3), "E");
        unSoretedMap.put(new Integer(4), "B");
        unSoretedMap.put(new Integer(5), "F");

        sorted_map.putAll(unSoretedMap);

        Object[] targetKeys={new Integer(2),new Integer(4),new Integer(3),new Integer(1),new Integer(5) };
        Object[] currecntKeys=sorted_map.keySet().toArray();

        assertArrayEquals(targetKeys,currecntKeys);
    }
}

zde je kód komparátoru mapy:

public static class MapStringDoubleComparator implements Comparator {

    Map<String, Double> base;

    public MapStringDoubleComparator(Map<String, Double> base) {
        this.base = base;
    }

    //note if you want decending in stead of ascending, turn around 1 and -1
    public int compare(Object a, Object b) {
        if ((Double) base.get(a) == (Double) base.get(b)) {
            return 0;
        } else if((Double) base.get(a) < (Double) base.get(b)) {
            return -1;
        }else{
            return 1;
        }
    }
}

a to je testcase pro toto:

@Test
public void testMapStringDoubleComparator(){
    HashMap<String, Double> unSoretedMap = new HashMap<String, Double>();
    Comparators.MapStringDoubleComparator bvc = new Comparators.MapStringDoubleComparator(
            unSoretedMap);
    TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
    //the testdata:
    unSoretedMap.put("D",new Double(67.3));
    unSoretedMap.put("A",new Double(99.5));
    unSoretedMap.put("B",new Double(67.4));
    unSoretedMap.put("C",new Double(67.5));
    unSoretedMap.put("E",new Double(99.5));

    sorted_map.putAll(unSoretedMap);

    Object[] targetKeys={"D","B","C","E","A"};
    Object[] currecntKeys=sorted_map.keySet().toArray();

    assertArrayEquals(targetKeys,currecntKeys);
}

toho, co můžete udělat, je mnohem obecnější, ale potřeboval jsem to jen pro 1 případ (Mapa)

11
michel.iamit

Namísto použití Collections.sort jako některých doporučuji použít Arrays.sort. Co vlastně dělá Collections.sort je něco takového:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    Object[] a = list.toArray();
    Arrays.sort(a);
    ListIterator<T> i = list.listIterator();
    for (int j=0; j<a.length; j++) {
        i.next();
        i.set((T)a[j]);
    }
}

Pouze volá toArray na seznamu a pak použije Arrays.sort. Tímto způsobem budou všechny položky mapy zkopírovány třikrát: jednou z mapy do dočasného seznamu (ať už je to LinkedList nebo ArrayList), pak do dočasného pole a nakonec do nové mapy.

Moje řešení tento krok vynechá, protože nevytváří zbytečný LinkedList. Zde je kód, obecně přátelský a výkonově optimální:

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) 
{
    @SuppressWarnings("unchecked")
    Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]);

    Arrays.sort(array, new Comparator<Map.Entry<K, V>>() 
    {
        public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) 
        {
            return e1.getValue().compareTo(e2.getValue());
        }
    });

    Map<K, V> result = new LinkedHashMap<K, V>();
    for (Map.Entry<K, V> entry : array)
        result.put(entry.getKey(), entry.getValue());

    return result;
}
9
ciamej

Toto je variace Anthonyho odpovědi, která nefunguje, pokud existují duplicitní hodnoty:

public static <K, V extends Comparable<V>> Map<K, V> sortMapByValues(final Map<K, V> map) {
    Comparator<K> valueComparator =  new Comparator<K>() {
        public int compare(K k1, K k2) {
            final V v1 = map.get(k1);
            final V v2 = map.get(k2);

            /* Not sure how to handle nulls ... */
            if (v1 == null) {
                return (v2 == null) ? 0 : 1;
            }

            int compare = v2.compareTo(v1);
            if (compare != 0)
            {
                return compare;
            }
            else
            {
                Integer h1 = k1.hashCode();
                Integer h2 = k2.hashCode();
                return h2.compareTo(h1);
            }
        }
    };
    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Všimněte si, že je to spíše ve vzduchu, jak zvládnout nuly. 

Jednou z důležitých výhod tohoto přístupu je, že ve skutečnosti vrátí mapu, na rozdíl od některých jiných nabízených řešení.

8
Roger

Hlavní problém. Pokud použijete první odpověď (Google vás sem přenese), změňte komparátor tak, aby přidal klauzuli o shodě, jinak nemůžete získat hodnoty z sort_map podle klíčů:

public int compare(String a, String b) {
        if (base.get(a) > base.get(b)) {
            return 1;
        } else if (base.get(a) < base.get(b)){
            return -1;
        } 

        return 0;
        // returning 0 would merge keys
    }
7
cuneyt

Nejlepší přístup

import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import Java.util.Set;
import Java.util.Map.Entry; 

public class OrderByValue {

  public static void main(String a[]){
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("Java", 20);
    map.put("C++", 45);
    map.put("Unix", 67);
    map.put("MAC", 26);
    map.put("Why this kolavari", 93);
    Set<Entry<String, Integer>> set = map.entrySet();
    List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
    Collections.sort( list, new Comparator<Map.Entry<String, Integer>>()
    {
        public int compare( Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2 )
        {
            return (o1.getValue()).compareTo( o2.getValue() );//Ascending order
            //return (o2.getValue()).compareTo( o1.getValue() );//Descending order
        }
    } );
    for(Map.Entry<String, Integer> entry:list){
        System.out.println(entry.getKey()+" ==== "+entry.getValue());
    }
  }}

Výstup

Java ==== 20

MAC ==== 26

C++ ==== 45

Unix ==== 67

Why this kolavari ==== 93
6
Nilesh Jadav

Na tuto otázku je již mnoho odpovědí, ale nikdo mi neposkytl to, co jsem hledal, implementaci mapy, která vrací klíče a položky seřazené podle přidružené hodnoty, a udržuje tuto vlastnost jako klíče a hodnoty v mapě. Dvě jinéotázky požádejte o to konkrétně. 

Uvařila jsem obecný příklad, který tento případ vyřeší. Tato implementace nerešpektuje všechny smlouvy rozhraní Map, jako například odrážení změn hodnot a odebrání v sadách návratu z keySet () a entrySet () v původním objektu. Cítil jsem, že takové řešení by bylo příliš velké, než aby zahrnulo odpověď Stack Overflow. Pokud se mi podaří vytvořit úplnější implementaci, možná ji pošlu do Githubu a pak do ní vložím aktualizovanou verzi této odpovědi.

import Java.util.*;

/**
 * A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
 * by associated values based on the the comparator provided at construction
 * time. The order of two or more keys with identical values is not defined.
 * <p>
 * Several contracts of the Map interface are not satisfied by this minimal
 * implementation.
 */
public class ValueSortedMap<K, V> extends HashMap<K, V> {
    protected Map<V, Collection<K>> valueToKeysMap;

    // uses natural order of value object, if any
    public ValueSortedMap() {
        this((Comparator<? super V>) null);
    }

    public ValueSortedMap(Comparator<? super V> valueComparator) {
        this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
    }

    public boolean containsValue(Object o) {
        return valueToKeysMap.containsKey(o);
    }

    public V put(K k, V v) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        super.put(k, v);
        if (!valueToKeysMap.containsKey(v)) {
            Collection<K> keys = new ArrayList<K>();
            keys.add(k);
            valueToKeysMap.put(v, keys);
        } else {
            valueToKeysMap.get(v).add(k);
        }
        return oldV;
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    public V remove(Object k) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            super.remove(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        return oldV;
    }

    public void clear() {
        super.clear();
        valueToKeysMap.clear();
    }

    public Set<K> keySet() {
        LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
        for (V v : valueToKeysMap.keySet()) {
            Collection<K> keys = valueToKeysMap.get(v);
            ret.addAll(keys);
        }
        return ret;
    }

    public Set<Map.Entry<K, V>> entrySet() {
        LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
        for (Collection<K> keys : valueToKeysMap.values()) {
            for (final K k : keys) {
                final V v = get(k);
                ret.add(new Map.Entry<K,V>() {
                    public K getKey() {
                        return k;
                    }

                    public V getValue() {
                        return v;
                    }

                    public V setValue(V v) {
                        throw new UnsupportedOperationException();
                    }
                });
            }
        }
        return ret;
    }
}
6
David Bleckmann

V závislosti na kontextu, pomocí Java.util.LinkedHashMap<T> který připomíná pořadí, ve kterém jsou položky umístěny do mapy. V opačném případě, pokud potřebujete třídit hodnoty na základě jejich přirozeného uspořádání, doporučuji zachovat samostatný seznam, který lze třídit pomocí Collections.sort().

5
Ryan Delucchi

ProtožeTreeMap <> nefungujepro hodnoty, které mohou být stejné, použil jsem toto:

private <K, V extends Comparable<? super V>> List<Entry<K, V>> sort(Map<K, V> map)     {
    List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
    Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
        public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    });

    return list;
}

Možná budete chtít umístitlistv souboruLinkedHashMap, ale pokud ho budete právě opakovat, je to zbytečné ...

5
malix

To je prostě příliš složité. Mapy neměly dělat takovou práci jako třídění podle hodnoty. Nejjednodušší způsob je vytvořit si vlastní třídu tak, aby vyhovovala vašim požadavkům.

V příkladu níže byste měli přidat TreeMap komparátor na místě, kde * je. Ale Java API dává komparátor pouze klíče, ne hodnoty. Všechny zde uvedené příklady jsou založeny na 2 mapách. Jeden Haš a jeden nový Strom. Což je zvláštní.

Příklad:

Map<Driver driver, Float time> map = new TreeMap<Driver driver, Float time>(*);

Změňte tak mapu na soubor takto:

ResultComparator rc = new ResultComparator();
Set<Results> set = new TreeSet<Results>(rc);

Vytvoříte třídu Results,

public class Results {
    private Driver driver;
    private Float time;

    public Results(Driver driver, Float time) {
        this.driver = driver;
        this.time = time;
    }

    public Float getTime() {
        return time;
    }

    public void setTime(Float time) {
        this.time = time;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver (Driver driver) {
        this.driver = driver;
    }
}

a třída Comparator:

public class ResultsComparator implements Comparator<Results> {
    public int compare(Results t, Results t1) {
        if (t.getTime() < t1.getTime()) {
            return 1;
        } else if (t.getTime() == t1.getTime()) {
            return 0;
        } else {
            return -1;
        }
    }
}

Tímto způsobem můžete snadno přidat více závislostí.

A jako poslední bod přidám jednoduchý iterátor:

Iterator it = set.iterator();
while (it.hasNext()) {
    Results r = (Results)it.next();
    System.out.println( r.getDriver().toString
        //or whatever that is related to Driver class -getName() getSurname()
        + " "
        + r.getTime()
        );
}
5
Darkless

Afaik je nejčistší způsob využití sbírek pro třídění map na hodnotu:

Map<String, Long> map = new HashMap<String, Long>();
// populate with data to sort on Value
// use datastructure designed for sorting

Queue queue = new PriorityQueue( map.size(), new MapComparable() );
queue.addAll( map.entrySet() );

// get a sorted map
LinkedHashMap<String, Long> linkedMap = new LinkedHashMap<String, Long>();

for (Map.Entry<String, Long> entry; (entry = queue.poll())!=null;) {
    linkedMap.put(entry.getKey(), entry.getValue());
}

public static class MapComparable implements Comparator<Map.Entry<String, Long>>{

  public int compare(Entry<String, Long> e1, Entry<String, Long> e2) {
    return e1.getValue().compareTo(e2.getValue());
  }
}
4
lisak

Zde je řešení OO (tj. Nepoužívá metody static):

import Java.util.Collections;
import Java.util.Comparator;
import Java.util.HashMap;
import Java.util.Iterator;
import Java.util.LinkedList;
import Java.util.LinkedHashMap;
import Java.util.List;
import Java.util.Map;

public class SortableValueMap<K, V extends Comparable<V>>
  extends LinkedHashMap<K, V> {
  public SortableValueMap() { }

  public SortableValueMap( Map<K, V> map ) {
    super( map );
  }

  public void sortByValue() {
    List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>( entrySet() );

    Collections.sort( list, new Comparator<Map.Entry<K, V>>() {
      public int compare( Map.Entry<K, V> entry1, Map.Entry<K, V> entry2 ) {
        return entry1.getValue().compareTo( entry2.getValue() );
      }
    });

    clear();

    for( Map.Entry<K, V> entry : list ) {
      put( entry.getKey(), entry.getValue() );
    }
  }

  private static void print( String text, Map<String, Double> map ) {
    System.out.println( text );

    for( String key : map.keySet() ) {
      System.out.println( "key/value: " + key + "/" + map.get( key ) );
    }
  }

  public static void main( String[] args ) {
    SortableValueMap<String, Double> map =
      new SortableValueMap<String, Double>();

    map.put( "A", 67.5 );
    map.put( "B", 99.5 );
    map.put( "C", 82.4 );
    map.put( "D", 42.0 );

    print( "Unsorted map", map );
    map.sortByValue();
    print( "Sorted map", map );
  }
}

Tím přispěl k public domain.

4
Dave Jarvis

Některé jednoduché změny, které mají uspořádanou mapu s dvojicemi, které mají duplicitní hodnoty. V metodě porovnání (třída ValueComparator), když jsou hodnoty stejné, nevracejte 0, ale vraťte výsledek porovnání dvou klíčů. Klíče jsou v mapě odlišné, takže se vám podaří udržet duplicitní hodnoty (které jsou tříděny podle klíčů na cestě). Výše uvedený příklad lze takto upravit takto:

    public int compare(Object a, Object b) {

        if((Double)base.get(a) < (Double)base.get(b)) {
          return 1;
        } else if((Double)base.get(a) == (Double)base.get(b)) {
          return ((String)a).compareTo((String)b);
        } else {
          return -1;
        }
      }
    }
4
dimkar

Na základě kódu @devinmoore, metody třídění map využívající generiky a podporující vzestupné i sestupné uspořádání.

/**
 * Sort a map by it's keys in ascending order. 
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map) {
    return sortMapByKey(map, SortingOrder.ASCENDING);
}

/**
 * Sort a map by it's values in ascending order.
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map) {
    return sortMapByValue(map, SortingOrder.ASCENDING);
}

/**
 * Sort a map by it's keys.
 *  
 * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. 
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByKey(final Map<K, V> map, final SortingOrder sortingOrder) {
    Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return comparableCompare(o1.getKey(), o2.getKey(), sortingOrder);
        }
    };

    return sortMap(map, comparator);
}

/**
 * Sort a map by it's values.
 *  
 * @param sortingOrder {@link SortingOrder} enum specifying requested sorting order. 
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMapByValue(final Map<K, V> map, final SortingOrder sortingOrder) {
    Comparator<Map.Entry<K, V>> comparator = new Comparator<Entry<K,V>>() {
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return comparableCompare(o1.getValue(), o2.getValue(), sortingOrder);
        }
    };

    return sortMap(map, comparator);
}

@SuppressWarnings("unchecked")
private static <T> int comparableCompare(T o1, T o2, SortingOrder sortingOrder) {
    int compare = ((Comparable<T>)o1).compareTo(o2);

    switch (sortingOrder) {
    case ASCENDING:
        return compare;
    case DESCENDING:
        return (-1) * compare;
    }

    return 0;
}

/**
 * Sort a map by supplied comparator logic.
 *  
 * @return new instance of {@link LinkedHashMap} contained sorted entries of supplied map.
 * @author Maxim Veksler
 */
public static <K, V> LinkedHashMap<K, V> sortMap(final Map<K, V> map, final Comparator<Map.Entry<K, V>> comparator) {
    // Convert the map into a list of key,value pairs.
    List<Map.Entry<K, V>> mapEntries = new LinkedList<Map.Entry<K, V>>(map.entrySet());

    // Sort the converted list according to supplied comparator.
    Collections.sort(mapEntries, comparator);

    // Build a new ordered map, containing the same entries as the old map.  
    LinkedHashMap<K, V> result = new LinkedHashMap<K, V>(map.size() + (map.size() / 20));
    for(Map.Entry<K, V> entry : mapEntries) {
        // We iterate on the mapEntries list which is sorted by the comparator putting new entries into 
        // the targeted result which is a sorted map. 
        result.put(entry.getKey(), entry.getValue());
    }

    return result;
}

/**
 * Sorting order enum, specifying request result sort behavior.
 * @author Maxim Veksler
 *
 */
public static enum SortingOrder {
    /**
     * Resulting sort will be from smaller to biggest.
     */
    ASCENDING,
    /**
     * Resulting sort will be from biggest to smallest.
     */
    DESCENDING
}
4
Maxim Veksler

Řešení Stephena je opravdu skvělé, ale pro ty, kteří nemohou Guavu používat:

Zde je moje řešení pro třídění podle hodnoty mapy. Toto řešení řeší případ, kdy je dvojnásobek stejné hodnoty atd.

// If you want to sort a map by value, and if there can be twice the same value:

// here is your original map
Map<String,Integer> mapToSortByValue = new HashMap<String, Integer>();
mapToSortByValue.put("A", 3);
mapToSortByValue.put("B", 1);
mapToSortByValue.put("C", 3);
mapToSortByValue.put("D", 5);
mapToSortByValue.put("E", -1);
mapToSortByValue.put("F", 1000);
mapToSortByValue.put("G", 79);
mapToSortByValue.put("H", 15);

// Sort all the map entries by value
Set<Map.Entry<String,Integer>> set = new TreeSet<Map.Entry<String,Integer>>(
        new Comparator<Map.Entry<String,Integer>>(){
            @Override
            public int compare(Map.Entry<String,Integer> obj1, Map.Entry<String,Integer> obj2) {
                Integer val1 = obj1.getValue();
                Integer val2 = obj2.getValue();
                // DUPLICATE VALUE CASE
                // If the values are equals, we can't return 0 because the 2 entries would be considered
                // as equals and one of them would be deleted (because we use a set, no duplicate, remember!)
                int compareValues = val1.compareTo(val2);
                if ( compareValues == 0 ) {
                    String key1 = obj1.getKey();
                    String key2 = obj2.getKey();
                    int compareKeys = key1.compareTo(key2);
                    if ( compareKeys == 0 ) {
                        // what you return here will tell us if you keep REAL KEY-VALUE duplicates in your set
                        // if you want to, do whatever you want but do not return 0 (but don't break the comparator contract!)
                        return 0;
                    }
                    return compareKeys;
                }
                return compareValues;
            }
        }
);
set.addAll(mapToSortByValue.entrySet());


// OK NOW OUR SET IS SORTED COOL!!!!

// And there's nothing more to do: the entries are sorted by value!
for ( Map.Entry<String,Integer> entry : set ) {
    System.out.println("Set entries: " + entry.getKey() + " -> " + entry.getValue());
}




// But if you add them to an hashmap
Map<String,Integer> myMap = new HashMap<String,Integer>();
// When iterating over the set the order is still good in the println...
for ( Map.Entry<String,Integer> entry : set ) {
    System.out.println("Added to result map entries: " + entry.getKey() + " " + entry.getValue());
    myMap.put(entry.getKey(), entry.getValue());
}

// But once they are in the hashmap, the order is not kept!
for ( Integer value : myMap.values() ) {
    System.out.println("Result map values: " + value);
}
// Also this way doesn't work:
// Logic because the entryset is a hashset for hashmaps and not a treeset
// (and even if it was a treeset, it would be on the keys only)
for ( Map.Entry<String,Integer> entry : myMap.entrySet() ) {
    System.out.println("Result map entries: " + entry.getKey() + " -> " + entry.getValue());
}


// CONCLUSION:
// If you want to iterate on a map ordered by value, you need to remember:
// 1) Maps are only sorted by keys, so you can't sort them directly by value
// 2) So you simply CAN'T return a map to a sortMapByValue function
// 3) You can't reverse the keys and the values because you have duplicate values
//    This also means you can't neither use Guava/Commons bidirectionnal treemaps or stuff like that

// SOLUTIONS
// So you can:
// 1) only sort the values which is easy, but you loose the key/value link (since you have duplicate values)
// 2) sort the map entries, but don't forget to handle the duplicate value case (like i did)
// 3) if you really need to return a map, use a LinkedHashMap which keep the insertion order

Exec: http://www.ideone.com/dq3Lu

Výstup:

Set entries: E -> -1
Set entries: B -> 1
Set entries: A -> 3
Set entries: C -> 3
Set entries: D -> 5
Set entries: H -> 15
Set entries: G -> 79
Set entries: F -> 1000
Added to result map entries: E -1
Added to result map entries: B 1
Added to result map entries: A 3
Added to result map entries: C 3
Added to result map entries: D 5
Added to result map entries: H 15
Added to result map entries: G 79
Added to result map entries: F 1000
Result map values: 5
Result map values: -1
Result map values: 1000
Result map values: 79
Result map values: 3
Result map values: 1
Result map values: 3
Result map values: 15
Result map entries: D -> 5
Result map entries: E -> -1
Result map entries: F -> 1000
Result map entries: G -> 79
Result map entries: A -> 3
Result map entries: B -> 1
Result map entries: C -> 3
Result map entries: H -> 15

Doufám, že to pomůže některým lidem

4
Sebastien Lorber

Můžete vyzkoušet multimapy Guavy:

TreeMap<Integer, Collection<String>> sortedMap = new TreeMap<>(
        Multimaps.invertFrom(Multimaps.forMap(originalMap), 
        ArrayListMultimap.<Integer, String>create()).asMap());

V důsledku toho získáte mapu z původních hodnot do kolekcí klíčů, které jim odpovídají. Tento přístup lze použít, i když existuje více klíčů pro stejnou hodnotu.

3

Pozdní vstup.

S příchodem Java-8 můžeme použít datové proudy pro manipulaci s daty velmi jednoduchým/stručným způsobem. Streamování můžete použít k třídění položek mapy podle hodnoty a vytvoření LinkedHashMap který zachovává iteraci vložení-pořadí.

Např:

LinkedHashMap sortedByValueMap = map.entrySet().stream()
                .sorted(comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey))     //first sorting by Value, then sorting by Key(entries with same value)
                .collect(LinkedHashMap::new,(map,entry) -> map.put(entry.getKey(),entry.getValue()),LinkedHashMap::putAll);

Pro obrácené řazení nahraďte:

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)

s

comparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey).reversed()
3
Pankaj Singhal

Spojila jsem řešení user157196 a Carter Page:

class MapUtil {

    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue( Map<K, V> map ){
        ValueComparator<K,V> bvc =  new ValueComparator<K,V>(map);
        TreeMap<K,V> sorted_map = new TreeMap<K,V>(bvc);
        sorted_map.putAll(map);
        return sorted_map;
    }

}

class ValueComparator<K, V extends Comparable<? super V>> implements Comparator<K> {

    Map<K, V> base;
    public ValueComparator(Map<K, V> base) {
        this.base = base;
    }

    public int compare(K a, K b) {
        int result = (base.get(a).compareTo(base.get(b)));
        if (result == 0) result=1;
        // returning 0 would merge keys
        return result;
    }
}
3
RobotMan

Pokud máte duplicitní klíče a pouze malou sadu dat (<1000) a váš kód není kritický k výkonu, můžete provést pouze následující:

Map<String,Integer> tempMap=new HashMap<String,Integer>(inputUnsortedMap);
LinkedHashMap<String,Integer> sortedOutputMap=new LinkedHashMap<String,Integer>();

for(int i=0;i<inputUnsortedMap.size();i++){
    Map.Entry<String,Integer> maxEntry=null;
    Integer maxValue=-1;
    for(Map.Entry<String,Integer> entry:tempMap.entrySet()){
        if(entry.getValue()>maxValue){
            maxValue=entry.getValue();
            maxEntry=entry;
        }
    }
    tempMap.remove(maxEntry.getKey());
    sortedOutputMap.put(maxEntry.getKey(),maxEntry.getValue());
}

inputUnsortedMap je vstupem do kódu.

Proměnná sortOutputMap bude při iteraci obsahovat data v sestupném pořadí. Chcete-li změnit pořadí, změňte> na <v příkazu if.

Není nejrychlejším tříděním, ale bez dalších závislostí.

3
nibor

Když jsem s tím konfrontován, jen vytvořím seznam na straně. Pokud je dáte dohromady do vlastní implementace Mapy, bude to mít hezký pocit ... Můžete použít něco jako následující, provádět to jen v případě potřeby. (Poznámka: Tohle jsem opravdu netestovala, ale kompilace ... by tam někde mohla být hloupá malá chyba)

(Pokud chcete, aby byla tříděna podle klíčů i hodnot, musí třída rozšířit TreeMap, nedefinujte metody přístupového systému a nechte mutátory zavolat super.xxxxx namísto map_.xxxx)

package com.javadude.sample;

import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;
import Java.util.Set;

public class SortedValueHashMap<K, V> implements Map<K, V> {
    private Map<K, V> map_ = new HashMap<K, V>();
    private List<V> valueList_ = new ArrayList<V>();
    private boolean needsSort_ = false;
    private Comparator<V> comparator_;

    public SortedValueHashMap() {
    }
    public SortedValueHashMap(List<V> valueList) {
        valueList_ = valueList;
    }

    public List<V> sortedValues() {
        if (needsSort_) {
            needsSort_ = false;
            Collections.sort(valueList_, comparator_);
        }
        return valueList_;
    }

    // mutators
    public void clear() {
        map_.clear();
        valueList_.clear();
        needsSort_ = false;
    }

    public V put(K key, V value) {
        valueList_.add(value);
        needsSort_ = true;
        return map_.put(key, value);
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        map_.putAll(m);
        valueList_.addAll(m.values());
        needsSort_ = true;
    }

    public V remove(Object key) {
        V value = map_.remove(key);
        valueList_.remove(value);
        return value;
    }

    // accessors
    public boolean containsKey(Object key)           { return map_.containsKey(key); }
    public boolean containsValue(Object value)       { return map_.containsValue(value); }
    public Set<Java.util.Map.Entry<K, V>> entrySet() { return map_.entrySet(); }
    public boolean equals(Object o)                  { return map_.equals(o); }
    public V get(Object key)                         { return map_.get(key); }
    public int hashCode()                            { return map_.hashCode(); }
    public boolean isEmpty()                         { return map_.isEmpty(); }
    public Set<K> keySet()                           { return map_.keySet(); }
    public int size()                                { return map_.size(); }
    public Collection<V> values()                    { return map_.values(); }
}
2

Tato metoda bude sloužit jen účelu. („neúspěchem“ je, že hodnoty musí implementovat rozhraní Java.util.Comparable)

  /**

 * Sort a map according to values.

 * @param <K> the key of the map.
 * @param <V> the value to sort according to.
 * @param mapToSort the map to sort.

 * @return a map sorted on the values.

 */ 
public static <K, V extends Comparable< ? super V>> Map<K, V>
sortMapByValues(final Map <K, V> mapToSort)
{
    List<Map.Entry<K, V>> entries =
        new ArrayList<Map.Entry<K, V>>(mapToSort.size());  

    entries.addAll(mapToSort.entrySet());

    Collections.sort(entries,
                     new Comparator<Map.Entry<K, V>>()
    {
        @Override
        public int compare(
               final Map.Entry<K, V> entry1,
               final Map.Entry<K, V> entry2)
        {
            return entry1.getValue().compareTo(entry2.getValue());
        }
    });      

    Map<K, V> sortedMap = new LinkedHashMap<K, V>();      

    for (Map.Entry<K, V> entry : entries)
    {
        sortedMap.put(entry.getKey(), entry.getValue());

    }      

    return sortedMap;

}

http://javawithswaranga.blogspot.com/2011/06/generic-method-to-sort-hashmap.html

2
didxga

Zde je kód Java 8 s AbacusUtil

Map<String, Integer> map = N.asMap("a", 2, "b", 3, "c", 1, "d", 2);
Map<String, Integer> sortedMap = Stream.of(map.entrySet()).sorted(Map.Entry.comparingByValue()).toMap(e -> e.getKey(), e -> e.getValue(),
    LinkedHashMap::new);
N.println(sortedMap);
// output: {c=1, a=2, d=2, b=3}

Prohlášení : Jsem vývojář AbacusUtil.

2
user_3380739

Nejjednodušší brute-force sortHashMap metoda pro HashMap<String, Long>: můžete ji prostě zkopírovat a použít takto:

public class Test  {
    public static void main(String[] args)  {
        HashMap<String, Long> hashMap = new HashMap<>();
        hashMap.put("Cat", (long) 4);
        hashMap.put("Human", (long) 2);
        hashMap.put("Dog", (long) 4);
        hashMap.put("Fish", (long) 0);
        hashMap.put("Tree", (long) 1);
        hashMap.put("Three-legged-human", (long) 3);
        hashMap.put("Monkey", (long) 2);

        System.out.println(hashMap);  //{Human=2, Cat=4, Three-legged-human=3, Monkey=2, Fish=0, Tree=1, Dog=4}
        System.out.println(sortHashMap(hashMap));  //{Cat=4, Dog=4, Three-legged-human=3, Human=2, Monkey=2, Tree=1, Fish=0}
    }

    public LinkedHashMap<String, Long> sortHashMap(HashMap<String, Long> unsortedMap)  {
        LinkedHashMap<String, Long> result = new LinkedHashMap<>();

        //add String keys to an array: the array would get sorted, based on those keys' values
        ArrayList<String> sortedKeys = new ArrayList<>();
        for (String key: unsortedMap.keySet())  {
            sortedKeys.add(key);
        }

        //sort the ArrayList<String> of keys    
        for (int i=0; i<unsortedMap.size(); i++)  {
            for (int j=1; j<sortedKeys.size(); j++)  {
                if (unsortedMap.get(sortedKeys.get(j)) > unsortedMap.get(sortedKeys.get(j-1))) {
                    String temp = sortedKeys.get(j);
                    sortedKeys.set(j, sortedKeys.get(j-1));
                    sortedKeys.set(j-1, temp);
                }
            }
        }

        // construct the result Map
        for (String key: sortedKeys)  {
            result.put(key, unsortedMap.get(key));
        }

        return result;
    }
}
1
parsecer

Přepsal jsem metodu devinmoore, která provádí třídění mapy podle její hodnoty bez použití Iterátoru:

public static Map<K, V> sortMapByValue(Map<K, V> inputMap) {

    Set<Entry<K, V>> set = inputMap.entrySet();
    List<Entry<K, V>> list = new ArrayList<Entry<K, V>>(set);

    Collections.sort(list, new Comparator<Map.Entry<K, V>>()
    {
        @Override
        public int compare(Entry<K, V> o1, Entry<K, V> o2) {
            return (o1.getValue()).compareTo( o2.getValue() );  //Ascending order
        }
    } );

    Map<K, V> sortedMap = new LinkedHashMap<>();

    for(Map.Entry<K, V> entry : list){
        sortedMap.put(entry.getKey(), entry.getValue());
    }

    return sortedMap;
}

Note:, které jsme použili jako výstupní mapu LinkedHashMap, protože náš seznam byl seřazen podle hodnoty a nyní bychom měli uložit náš seznam do výstupní mapy s pořadí vložených klíčů, hodnot. Pokud tedy jako mapu výstupu použijete například TreeMap, mapa bude opět rozdělena podle mapových klíčů!

Toto je hlavní metoda:

public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    map.put("3", "three");
    map.put("1", "one");
    map.put("5", "five");
    System.out.println("Input Map:" + map);
    System.out.println("Sorted Map:" + sortMapByValue(map));
}

Konečně je to výstup:

Input Map:{1=one, 3=three, 5=five}
Sorted Map:{5=five, 1=one, 3=three}
0
smart-developer

Moje řešení je poměrně jednoduchý přístup ve způsobu použití většinou zadaných API. Pomocí funkce Map exportujeme její obsah jako Set via entrySet () method . Nyní máme Set obsahující Map.Entry objekty. 

Okay, Set nenese objednávku, ale můžeme obsah vzít a dát do ArrayList . Nyní má náhodné pořadí, ale stejně to budeme řadit.

Jako ArrayList is a Collection , nyní používáme metodu Collections.sort () /, která přivede pořádek do chaosu. Protože naše Map.Entry objekty si neuvědomují, jaký druh srovnání potřebujeme, poskytujeme vlastní komparátor .

public static void main(String[] args) {
    HashMap<String, String> map = new HashMap<>();
    map.put("Z", "E");
    map.put("G", "A");
    map.put("D", "C");
    map.put("E", null);
    map.put("O", "C");
    map.put("L", "D");
    map.put("Q", "B");
    map.put("A", "F");
    map.put(null, "X");
    MapEntryComparator mapEntryComparator = new MapEntryComparator();

    List<Entry<String,String>> entryList = new ArrayList<>(map.entrySet());
    Collections.sort(entryList, mapEntryComparator);

    for (Entry<String, String> entry : entryList) {
        System.out.println(entry.getKey() + " : " + entry.getValue());
    }

}
0
Alexander

Pokud je preferováno mít strukturu dat Map, která přirozeně třídí podle hodnot, aniž by musela spouštět jakékoli metody třídění nebo explicitně předávat nástroj, mohou být použitelná následující řešení:

(1) org.drools.chance.core.util.ValueSortedMap (projekt JBoss) udržuje dvě mapy interně jednu pro vyhledávání a jednu pro udržení tříděných hodnot. Podobně jako dříve přidané odpovědi, ale pravděpodobně je to část abstrakce a zapouzdření (včetně kopírovacího mechanismu), která z ní dělá bezpečnější použití zvenčí. 

(2) http://techblog.molindo.at/2008/11/Java-map-sorted-by-value.html vyhýbá se udržování dvou map a místo toho se spoléhá/rozšiřuje z linkedapap společnosti Apache Common. (Poznámka autora blogu: as all the code here is in the public domain):

// required to access LinkEntry.before and LinkEntry.after
package org.Apache.commons.collections.map;

// SNIP: imports

/**
* map implementation based on LinkedMap that maintains a sorted list of
* values for iteration
*/
public class ValueSortedHashMap extends LinkedMap {
    private final boolean _asc;

    // don't use super()!
    public ValueSortedHashMap(final boolean asc) {
        super(DEFAULT_CAPACITY);
        _asc = asc;
    }

    // SNIP: some more constructors with initial capacity and the like

    protected void addEntry(final HashEntry entry, final int hashIndex) {
        final LinkEntry link = (LinkEntry) entry;
        insertSorted(link);
        data[hashIndex] = entry;
    }

    protected void updateEntry(final HashEntry entry, final Object newValue) {
        entry.setValue(newValue);
        final LinkEntry link = (LinkEntry) entry;
        link.before.after = link.after;
        link.after.before = link.before;
        link.after = link.before = null;
        insertSorted(link);
    }

    private void insertSorted(final LinkEntry link) {
        LinkEntry cur = header;
        // iterate whole list, could (should?) be replaced with quicksearch
        // start at end to optimize speed for in-order insertions
        while ((cur = cur.before) != header & amp; & amp; !insertAfter(cur, link)) {}
        link.after = cur.after;
        link.before = cur;
        cur.after.before = link;
        cur.after = link;
    }

    protected boolean insertAfter(final LinkEntry cur, final LinkEntry link) {
        if (_asc) {
            return ((Comparable) cur.getValue())
            .compareTo((V) link.getValue()) & lt; = 0;
        } else {
            return ((Comparable) cur.getValue())
            .compareTo((V) link.getValue()) & gt; = 0;
        }
    }

    public boolean isAscending() {
        return _asc;
    }
}

(3) Napište vlastní Map nebo se rozšíří z LinkedHashMap, které se bude třídit pouze během výčtu (např. values(), keyset(), entryset()) podle potřeby. Vnitřní implementace/chování je odebráno z jedné pomocí této třídy, ale klientovi této třídy se zdá, že hodnoty jsou vždy tříděny, když jsou požadovány pro výčet. Tato třída doufá, že třídění bude probíhat většinou jednou, pokud byly všechny operace put dokončeny před výčtem. Metoda třídění přijímá některé z předchozích odpovědí na tuto otázku. 

public class SortByValueMap<K, V> implements Map<K, V> {

    private boolean isSortingNeeded = false;

    private final Map<K, V> map = new LinkedHashMap<>();

    @Override
    public V put(K key, V value) {
        isSortingNeeded = true;
        return map.put(key, value);
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> map) {
        isSortingNeeded = true;
        map.putAll(map);
    }

    @Override
    public Set<K> keySet() {
        sort();
        return map.keySet();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        sort();
        return map.entrySet();
    }

    @Override
    public Collection<V> values() {
        sort();
        return map.values();
    }

    private void sort() {
        if (!isSortingNeeded) {
            return;
        }

        List<Entry<K, V>> list = new ArrayList<>(size());

        for (Iterator<Map.Entry<K, V>> it = map.entrySet().iterator(); it.hasNext();) {
            Map.Entry<K, V> entry = it.next();
            list.add(entry);
            it.remove();
        }

        Collections.sort(list);

        for (Entry<K, V> entry : list) {
            map.put(entry.getKey(), entry.getValue());
        }

        isSortingNeeded = false;
    }

    @Override
    public String toString() {
        sort();
        return map.toString();
    }
}

(4) Guava nabízí ImmutableMap.Builder.orderEntriesByValue (Comparator valueComparator) ačkoliv výsledná mapa bude neměnná:

Konfiguruje tento Tvůrce, aby zadával položky podle hodnoty podle Specifikovaného komparátoru.

Pořadí řazení je stabilní, tzn. Pokud dvě položky mají hodnoty, které Porovnávají jako rovnocenné, položka, která byla vložena jako první, bude první V pořadí iterace vytvořené mapy.

0
Kenston Choi