160 votes

TreeMap trier par valeur

Je suis nouveau sur java, je veux écrire un comparateur à qui me permettra de trier TreeMap par valeur au lieu de la valeur par défaut tri naturel. j'ai essayé quelque chose comme ça, mais ne peut pas trouver ce qui ne va pas:

import java.util.*;

class treeMap {
    public static void main(String[] args) {
        System.out.println("the main");
        byValue cmp = new byValue();
        Map<String, Integer> map = new TreeMap<String, Integer>(cmp);
        map.put("de",10);
        map.put("ab", 20);
        map.put("a",5);

        for (Map.Entry<String,Integer> pair: map.entrySet()) {
            System.out.println(pair.getKey()+":"+pair.getValue());
        }
    }
}

class byValue implements Comparator<Map.Entry<String,Integer>> {
    public int compare(Map.Entry<String,Integer> e1, Map.Entry<String,Integer> e2) {
        if (e1.getValue() < e2.getValue()){
            return 1;
        } else if (e1.getValue() == e2.getValue()) {
            return 0;
        } else {
            return -1;
        }
    }
}

Je suppose que suis-je demande est ce que les contrôles de ce que se passe au comparateur de fonction, puis-je obtenir une Carte.Passe d'entrée du comparateur?

189voto

polygenelubricants Points 136838

Vous ne pouvez pas avoir l' TreeMap lui-même le tri sur les valeurs, qui défie l' SortedMap spécifications:

Un Map qui fournit, en outre, un total de commander sur ses touches.

Cependant, à l'aide d'une collection, vous pouvez toujours trier Map.entrySet() toutefois vous le souhaitez, que ce soit par des clés, valeurs, ou même une combinaison des deux(!!) de la deux.

Voici une méthode générique qui renvoie un SortedSet de Map.Entry, compte tenu d'un Map dont les valeurs sont Comparable:

static <K,V extends Comparable<? super V>>
SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
    SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
        new Comparator<Map.Entry<K,V>>() {
            @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                int res = e1.getValue().compareTo(e2.getValue());
                return res != 0 ? res : 1;
            }
        }
    );
    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
}

Maintenant, vous pouvez effectuer les opérations suivantes:

    Map<String,Integer> map = new TreeMap<String,Integer>();
    map.put("A", 3);
    map.put("B", 2);
    map.put("C", 1);   

    System.out.println(map);
    // prints "{A=3, B=2, C=1}"
    System.out.println(entriesSortedByValues(map));
    // prints "[C=1, B=2, A=3]"

Notez que des trucs funky qui se passera si vous essayez de modifier le SortedSet lui-même, ou l' Map.Entry à l'intérieur, parce que ce n'est plus une "vue" de la carte originale, comme entrySet() .

En règle générale, la nécessité de les trier d'une carte d'entrées par ses valeurs est atypique.


Note sur == pour Integer

Votre comparateur original compare Integer l'aide ==. C'est presque toujours mal, depuis == avec Integer des opérandes est une référence à l'égalité, pas la valeur de l'égalité.

    System.out.println(new Integer(0) == new Integer(0)); // prints "false"!!!

Questions connexes

77voto

Olof Larsson Points 636

polygenelubricants réponse est presque parfait. Il a un bug important. Elle ne le sera pas de la carte les entrées où les valeurs sont les mêmes.

Ce code:...

Map<String, Integer> nonSortedMap = new HashMap<String, Integer>();
nonSortedMap.put("ape", 1);
nonSortedMap.put("pig", 3);
nonSortedMap.put("cow", 1);
nonSortedMap.put("frog", 2);

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

De sortie:

ape:1
frog:2
pig:3

Notez comment notre vache a disparu comme elle a partagé la valeur "1" avec notre ape :O!

Cette modification du code résout ce problème:

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
        SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
            new Comparator<Map.Entry<K,V>>() {
                @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                    int res = e1.getValue().compareTo(e2.getValue());
                    return res != 0 ? res : 1; // Special fix to preserve items with equal values
                }
            }
        );
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

61voto

Vitalii Fedorenko Points 17469

Dans Java 8 (à l'aide d'une carte supplémentaire):

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

20voto

Michael Borgwardt Points 181658

Un TreeMap est toujours triés par les touches, rien est impossible. Un Comparator simple vous permet de contrôler la façon dont les touches sont triés.

Si vous voulez le tri des valeurs, vous devez les extraire dans un List et de tri.

10voto

Joachim Sauer Points 133411

Cela ne peut être fait à l'aide d'un Comparator, comme il le fera toujours obtenir la clé de la carte à comparer. TreeMap permet de trier uniquement par la clé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X