Trier une carte par valeurs


Je suis relativement nouveau sur Java, et trouve souvent que j'ai besoin de trier un Map<Key, Value> sur les valeurs.

Puisque les valeurs ne sont pas uniques, je me retrouve à convertir le keySet en array et à trier ce tableau par array sort avec un comparateur personnalisé qui trie sur la valeur associée à la clé.

Existe-t-il un moyen plus simple?

Author: Lii, 2008-09-21

30 answers

Voici une version générique:

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;
    }
}
 794
Author: Carter Page, 2018-04-03 00:00:27

Remarque Importante:

Ce code peut se casser de plusieurs façons. Si vous avez l'intention d'utiliser le code fourni, assurez-vous de lire les commentaires, comme bien d'être conscient des conséquences. Par exemple, les valeurs ne peuvent plus être récupérées par leur clé. (get renvoie toujours null.)


Il semble beaucoup plus facile que tout ce qui précède. Utilisez un TreeMap comme suit:

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
    }
}

Sortie:

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}
 405
Author: user157196, 2016-04-18 23:41:20

Java 8 offre une nouvelle réponse: convertissez les entrées en flux et utilisez les combinateurs de comparateur de Map.Entrée:

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

Cela vous permettra de consommer les entrées triées par ordre croissant de valeur. Si vous voulez une valeur décroissante, inversez simplement le comparateur:

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

Si les valeurs ne sont pas comparables, vous pouvez passer un comparateur explicite:

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

Vous pouvez ensuite utiliser d'autres opérations de flux pour consommer les données. Par exemple, si vous voulez le top 10 dans un nouvelle carte:

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));

Ou imprimer sur System.out:

map.entrySet().stream()
   .sorted(Map.Entry.comparingByValue())
   .forEach(System.out::println);
 230
Author: Brian Goetz, 2017-11-25 22:16:36

Trois réponses de 1 ligne...

Je voudrais utiliser Google Collections Goyave pour ce faire, si vos valeurs sont Comparable, alors vous pouvez utiliser

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

Qui créera une fonction (objet) pour la carte [qui prend l'une des clés en entrée, renvoyant la valeur respective], puis leur appliquera un ordre naturel (comparable) [les valeurs].

S'ils ne sont pas comparables, alors vous devrez faire quelque chose le long des lignes de

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

Ceux-ci peuvent être appliquées à un TreeMap (comme Ordering étend Comparator), ou un LinkedHashMap après un certain tri

NB: Si vous allez utiliser un TreeMap, rappelez-vous que si une comparaison == 0, alors l'élément est déjà dans la liste (ce qui se produira si vous avez plusieurs valeurs de comparer le même). Pour atténuer cela, vous pouvez ajouter votre clé au comparateur comme ceci (en supposant que vos clés et vos valeurs sont Comparable):

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

= Appliquer ordre naturel de la valeur mappée par la clé, et composé avec l'ordre naturel de la clé

Notez que cela ne fonctionnera toujours pas si vos clés se comparent à 0, mais cela devrait être suffisant pour la plupart des éléments comparable (comme hashCode, equals et compareTo sont souvent synchronisés...)

Voir Commande.onResultOf () et Fonctions.forMap () .

Mise en œuvre

Donc, maintenant que nous avons obtenu un comparateur qui fait ce que nous voulons, nous devons obtenir un en résultent.

map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);

Maintenant, cela fonctionnera très probablement, mais:

  1. doit être fait étant donné une carte complète
  2. N'essayez pas les comparateurs ci-dessus sur un TreeMap; il ne sert à rien d'essayer de comparer une clé insérée quand elle n'a pas de valeur avant la mise, c'est-à-dire qu'elle se cassera très vite

Le point 1 est un peu un facteur de rupture pour moi; google collections est incroyablement paresseux (ce qui est bien: vous pouvez faire à peu près toutes les opérations dans un instant; le vrai travail est fait lorsque vous commencez à utiliser le résultat), et cela nécessite de copier une carte entière!

Réponse"complète" / Carte triée en direct par valeurs

Ne vous inquiétez pas cependant; si vous étiez assez obsédé par le fait d'avoir une carte "en direct" triée de cette manière, vous pourriez résoudre non pas une mais les deux(!) des problèmes ci-dessus avec quelque chose de fou comme ce qui suit:

Remarque: Cela a considérablement changé en juin 2012 - le code précédent ne pourrait jamais fonctionner: un HashMap est nécessaire pour la recherche de l'valeurs sans créer une boucle infinie entre l'TreeMap.get() -> compare() et 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"));
    }
 }

Lorsque nous mettons, nous nous assurons que la carte de hachage a la valeur pour le comparateur, puis mis à l'arborescence pour le tri. Mais avant cela, nous vérifions la carte de hachage pour voir que la clé n'est pas en fait un doublon. De plus, le comparateur que nous créons inclura également la clé afin que les valeurs dupliquées ne suppriment pas les clés non dupliquées (en raison de == comparaison). Ces 2 éléments sontessentiels pour s'assurer que le contrat de carte est maintenu; si vous pensez que vous ne voulez pas cela, alors vous êtes presque au point d'inverser complètement la carte (à Map<V,K>).

Le constructeur devrait être appelé comme

 new ValueComparableMap(Ordering.natural());
 //or
 new ValueComparableMap(Ordering.from(comparator));
 207
Author: Stephen, 2017-05-23 12:26:34

À Partir de 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;
}
 174
Author: devinmoore, 2016-06-14 07:51:07

Avec Java 8, vous pouvez utiliser l'api streams {[3] } pour le faire de manière beaucoup moins verbeuse:

Map<K, V> sortedMap = map.entrySet().stream()
                         .sorted(Entry.comparingByValue())
                         .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
 29
Author: assylias, 2018-09-18 06:04:52

Le tri des clés nécessite que le comparateur recherche chaque valeur pour chaque comparaison. Une solution plus évolutive utiliserait directement l'entrySet, puisque la valeur serait immédiatement disponible pour chaque comparaison (bien que je n'ai pas sauvegardé cela par des chiffres).

Voici une version générique d'une telle chose:

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());
    }
}

Il existe des moyens de réduire la rotation de la mémoire pour la solution ci-dessus. La première ArrayList créée pourrait par exemple être réutilisée comme valeur de retour; cela nécessite la suppression de certains avertissements génériques, mais cela pourrait en valoir la peine pour le code de bibliothèque réutilisable. De plus, le comparateur n'a pas besoin d'être réaffecté à chaque appel.

Voici une version plus efficace mais moins attrayante:

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();

Enfin, si vous avez besoin d'accéder en continu aux informations triées (plutôt que de les trier de temps en temps), vous pouvez utiliser une carte multiple supplémentaire. Laissez-moi savoir si vous avez besoin de plus de détails...

 28
Author: volley, 2008-10-01 21:02:28

La bibliothèque commons-collections contient une solution appelée TreeBidiMap. Ou, vous pouvez jeter un oeil à l'API Google Collections. Il a TreeMultimap que vous pouvez utiliser.

Et si vous ne voulez pas utiliser ces cadre... ils viennent avec le code source.

 25
Author: p3t0r, 2011-05-30 18:59:51

J'ai regardé les réponses données, mais beaucoup d'entre elles sont plus compliquées que nécessaire ou suppriment des éléments de carte lorsque plusieurs clés ont la même valeur.

Voici une solution qui, je pense, convient mieux:

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;
}

Notez que la carte est triée de la valeur la plus élevée à la valeur la plus basse.

 23
Author: Anthony, 2012-02-25 16:44:42

Pour ce faire avec les nouvelles fonctionnalités de 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());
}

Les entrées sont ordonnées par leurs valeurs en utilisant le comparateur donné. Sinon, si vos valeurs sont mutuellement comparables, aucun comparateur explicite n'est nécessaire:

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

La liste renvoyée est un instantané de la carte donnée au moment où cette méthode est appelée, donc aucune ne reflétera les modifications ultérieures de l'autre. Pour une vue itérable en direct de la carte:

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

L'itérable retourné crée un nouvel instantané de la carte donnée chaque fois qu'elle est itérée, donc sauf modification simultanée, elle reflétera toujours l'état actuel de la carte.

 17
Author: gdejohn, 2014-06-24 22:16:19

Bien que je convienne que le besoin constant de trier une carte est probablement une odeur, je pense que le code suivant est le moyen le plus simple de le faire sans utiliser une structure de données différente.

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());
    }
}

}

Et voici un test unitaire embarrassant et incomplet:

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());
}

}

Le résultat est une liste triée de Map.Objets d'entrée, à partir desquels vous pouvez obtenir les clés et les valeurs.

 14
Author: Lyudmil, 2008-09-24 00:58:27

Créez un comparateur personnalisé et utilisez-le lors de la création d'un nouvel objet 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));

    }
}

Utilisez le code ci-dessous dans votre fonction principale

    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);

Sortie:

{B=75, D=50, C=50, A=35}
 14
Author: Sujan Reddy A, 2013-02-10 06:10:09

La réponse votée pour le plus ne fonctionne pas lorsque vous avez 2 éléments égaux. le TreeMap laisse les valeurs égales.

L'exemple: carte non triée

key/value: D/67.3
key/value: A/99.5
key/value: B/67.4
key/value: C/67.5
key/value: E/99.5

Résultats

key/value: A/99.5
key/value: C/67.5
key/value: B/67.4
key/value: D/67.3

Donc laisse de côté E!!

Pour moi, cela a bien fonctionné pour ajuster le comparateur, s'il est égal à ne pas retourner 0 mais -1.

Dans l'exemple:

La classe ValueComparator implémente le comparateur {

Base de la carte; comparateur de valeur public(Base de carte) { ce.base = base; }

Public int compare (Objet a, Objet 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;
}

} }

Maintenant, il renvoie:

Carte non triée:

key/value: D/67.3
key/value: A/99.5
key/value: B/67.4
key/value: C/67.5
key/value: E/99.5

Résultats:

key/value: A/99.5
key/value: E/99.5
key/value: C/67.5
key/value: B/67.4
key/value: D/67.3

En réponse aux étrangers (2011 nov. 22): J'utilise cette solution pour une carte de Integer Id et les noms, mais l'idée est la même, donc peut-être le code ci-dessus n'est pas correct (je vais l'écrire dans un test et vous donner le bon code), c'est le code pour un tri cartographique, basé sur la solution ci-dessus:

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;
        }
    }


}

Et c'est la classe de test (je viens de la tester, et cela fonctionne pour l'entier, 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);
    }
}

Voici le code pour le comparateur d'une carte:

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;
        }
    }
}

Et ceci est le cas de test pour ceci:

@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);
}

Bien sûr, vous pouvez rendre cela beaucoup plus générique, mais j'en avais juste besoin pour 1 cas (la carte)

 11
Author: michel.iamit, 2011-11-23 13:11:46

Utilisez un comparateur générique tel que:

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
Author: RoyalBigorno, 2014-09-09 06:43:09

Au lieu d'utiliser Collections.sort comme certains le font, je suggère d'utiliser Arrays.sort. En fait, ce que Collections.sort fait est quelque chose comme ceci:

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]);
    }
}

, Il appelle juste toArray sur la liste, puis utilise Arrays.sort. De cette façon, toutes les entrées de carte seront copiées trois fois: une fois de la carte vers la liste temporaire (que ce soit une LinkedList ou une ArrayList), puis vers le tableau temporaire et enfin vers la nouvelle carte.

Ma solution omet cette étape car elle ne crée pas de liste de liens inutile. Voici le code, générique-friendly et performance-optimale:

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
Author: ciamej, 2012-05-30 00:47:27

Ceci est une variante de la réponse d'Anthony, qui ne fonctionne pas s'il y a des valeurs en double:

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;
}

Notez que c'est plutôt dans l'air comment gérer les nuls.

Un avantage important de cette approche est qu'elle renvoie réellement une carte, contrairement à certaines des autres solutions proposées ici.

 8
Author: Roger, 2011-02-09 10:21:25

Problème majeur. Si vous utilisez la première réponse (Google vous emmène ici), modifiez le comparateur pour ajouter une clause equal, sinon vous ne pouvez pas obtenir de valeurs à partir du sorted_map par clés:

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
Author: cuneyt, 2012-11-18 06:37:50

Il y a déjà beaucoup de réponses à cette question, mais aucune ne m'a fourni ce que je cherchais, une implémentation de map qui renvoie des clés et des entrées triées par la valeur associée, et maintient cette propriété car les clés et les valeurs sont modifiées dans la carte. Deux autre les questions demandent cela spécifiquement.

J'ai préparé un exemple générique convivial qui résout ce cas d'utilisation. Cette implémentation n'honore pas tous les contrats de l'interface Map, tels que refléter les changements de valeur et les suppressions dans les ensembles renvoyés par keySet () et entrySet () dans l'objet d'origine. Je sentais qu'une telle solution serait trop grande pour être incluse dans une réponse de débordement de pile. Si je parviens à créer une implémentation plus complète, je la posterai peut-être sur Github, puis sur un lien dans une version mise à jour de cette réponse.

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
Author: David Bleckmann, 2017-06-05 21:36:27

Depuis TreeMap ne fonctionne pas pour les valeurs qui peut être égal, j'ai utilisé ceci:

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;
}

Vous pourriez mettre liste dans un LinkedHashMap, mais si vous allez seulement pour itérer sur lui tout de suite, c'est superflu...

 5
Author: malix, 2011-08-31 20:40:25

C'est trop compliqué. Les cartes n'étaient pas censées faire un travail tel que les trier par valeur. Le moyen le plus simple est de créer votre propre classe afin qu'elle corresponde à vos besoins.

Dans l'exemple inférieur, vous êtes censé ajouter TreeMap un comparateur à l'endroit où se trouve*. Mais par l'API java, il ne donne que des clés de comparateur, pas des valeurs. Tous les exemples indiqués ici sont basés sur 2 cartes. Un hachage et un nouvel arbre. Ce qui est étrange.

L'exemple:

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

Donc changer la carte dans un ensemble ce façon:

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

, Vous allez créer la classe 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;
    }
}

Et la classe de comparaison:

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;
        }
    }
}

De cette façon, vous pouvez facilement ajouter plus de dépendances.

Et comme dernier point, je vais ajouter un itérateur simple:

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
Author: Darkless, 2011-12-29 21:33:51

Meilleure Approche

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());
    }
  }}

Sortie

java ==== 20

MAC ==== 26

C++ ==== 45

Unix ==== 67

Why this kolavari ==== 93
 5
Author: Nilesh Jadav, 2016-12-05 22:29:17

En fonction du contexte, en utilisant java.util.LinkedHashMap<T> qui mémorise l'ordre dans lequel les éléments sont placés dans la carte. Sinon, si vous devez trier les valeurs en fonction de leur ordre naturel, je recommanderais de maintenir une liste distincte qui peut être triée via Collections.sort().

 4
Author: Ryan Delucchi, 2008-09-20 21:07:49

Basé sur le code @devinmoore, une carte triant des méthodes utilisant des génériques et prenant en charge l'ordre croissant et décroissant.

/**
 * 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
Author: Maxim Veksler, 2009-04-14 13:44:29

Voici une solution OO (c'est-à-dire qu'elle n'utilise pas de méthodes 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 );
  }
}

Par la présente fait don au domaine public.

 4
Author: Dave Jarvis, 2011-01-02 05:53:22

Afaik le moyen le plus propre consiste à utiliser des collections pour trier la carte sur la valeur:

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
Author: lisak, 2011-06-08 15:23:04

Quelques modifications simples afin d'avoir une carte triée avec des paires qui ont des valeurs en double. Dans la méthode compare (classe ValueComparator) lorsque les valeurs sont égales, ne renvoyez pas 0 mais renvoyez le résultat de la comparaison des 2 clés. Les clés sont distinctes dans une carte, vous réussissez donc à conserver les valeurs en double (qui sont triées par clés en passant). Ainsi l'exemple ci-dessus pourrait être modifié comme ceci:

    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
Author: dimkar, 2011-09-01 09:53:33

Bien sûr, la solution de Stephen est vraiment géniale, mais pour ceux qui ne peuvent pas utiliser la goyave:

Voici ma solution pour trier par valeur une carte. Cette solution gère le cas où il y a deux fois la même valeur, etc...

// 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

L'exec: http://www.ideone.com/dq3Lu

La sortie:

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

J'espère que cela aidera certaines personnes

 4
Author: Sebastien Lorber, 2011-12-27 17:43:44

Si vous avez des clés en double et seulement un petit ensemble de données (

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 est l'entrée du code.

La variable sortedOutputMap contiendra les données dans l'ordre décroissant lors de l'itération. Pour changer d'ordre, changez simplement > en a

N'est pas le tri le plus rapide mais fait le travail sans dépendances supplémentaires.

 3
Author: nibor, 2011-09-26 21:39:32

J'ai fusionné les solutions de user157196 et 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
Author: RobotMan, 2014-04-22 09:09:59

Vous pouvez essayer les multimaps de Goyava:

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

En conséquence, vous obtenez une carte des valeurs d'origine aux collections de clés qui leur correspondent. Cette approche peut être utilisée même s'il existe plusieurs clés pour la même valeur.

 3
Author: Vitalii Fedorenko, 2014-11-29 19:46:28