Carte ordonnée Java

322

En Java, existe-t-il un objet qui agit comme une carte pour stocker et accéder aux paires clé / valeur, mais peut renvoyer une liste ordonnée de clés et une liste ordonnée de valeurs, de sorte que les listes de clés et de valeurs soient dans le même ordre?

Donc, en tant qu'explication par code, je cherche quelque chose qui se comporte comme mon OrderedMap fictif:

OrderedMap<Integer, String> om = new OrderedMap<>();
om.put(0, "Zero");
om.put(7, "Seven");

String o = om.get(7); // o is "Seven"
List<Integer> keys = om.getKeys();
List<String> values = om.getValues();

for(int i = 0; i < keys.size(); i++)
{
    Integer key = keys.get(i);
    String value = values.get(i);
    Assert(om.get(key) == value);
}
Qu'est ce que c'est
la source
4
Si tout ce que vous voulez faire est de parcourir les deux en même temps, alors Map.entrySet () vous permettra de le faire sur n'importe quelle carte. Le LinkedHashMap a un ordre bien défini, mais pour toute carte, l'ensemble d'entrées reflète les paires clé / valeur.
Pete Kirkham
5
Ce code n'est pas un bon exemple car toute implémentation de Map se comportera comme votre exemple de code. triés, ordonnés ou non.
Peter Lawrey
2
Dans l'implémentation Sun JDK, les ensembles renvoyés par les ensembles getKeys et getValues ​​() sont soutenus par entrySet () dans la carte, ils auront donc le même ordre d'itération, ce que teste votre exemple.
Pete Kirkham
4
Eh bien, c'est intéressant, je ne l'ai jamais remarqué. Encore, appelez-moi fou, mais je préfère ne pas faire d'hypothèses sur l'implémentation qui ne soient pas explicitement vérifiées par l'interface. J'ai été brûlé trop de fois en faisant ça dans le passé.
Whatsit
2
Cela devrait être nommé Java Sorted Map, car Ordered Map est quelque chose de différent - voir LinkedHashMap.
Ondra Žižka

Réponses:

398

L' interface SortedMap (avec l'implémentation TreeMap ) devrait être votre ami.

L'interface a les méthodes:

  • keySet() qui renvoie un ensemble de clés dans l'ordre croissant
  • values() qui retourne une collection de toutes les valeurs dans l'ordre croissant des clés correspondantes

Cette interface répond donc exactement à vos besoins. Cependant, les clés doivent avoir un ordre significatif. Sinon, vous pouvez utiliser le LinkedHashMap où l'ordre est déterminé par l'ordre d'insertion.

dmeister
la source
2
exemple: SortedMap <String, Object> map = new TreeMap <> ();
Ben
7
Pour utiliser TreeMap, il fallait que la classe de clé implémente une interface comparable. Sinon, une sorte d'exception RuntimeException sera levée. TreeMap c'est aussi une carte triée, mais je pense que l'auteur veut utiliser une carte ordonnée (non triée). LinkedHashMap, c'est un bon choix pour obtenir uniquement la carte ordonnée (comme vous l'avez dit, "déterminée par l'ordre d'insertion").
K. Gol
1
cette réponse pourrait être améliorée en montrant comment itérer sur keySet ()
4
De java 8 doc . LinkedHashMapdont l'ordre d'itération est l' ordre dans lequel ses entrées ont été consultées pour la dernière fois
TRiNE
1
@TRiNE Je ne suis pas votre commentaire, mais j'ai peut-être manqué un peu de contexte. Par défaut LinkedHashMap, l'ordre d'itération est l'ordre d'insertion, mais vous pouvez utiliser un constructeur différent pour spécifier à la place l'ordre d'accès. docs.oracle.com/javase/8/docs/api/java/util/…
vol
212

Existe-t-il un objet qui agit comme une carte pour stocker et accéder aux paires clé / valeur, mais peut renvoyer une liste ordonnée de clés et une liste ordonnée de valeurs, de sorte que les listes de clés et de valeurs soient dans le même ordre?

Vous recherchez java.util.LinkedHashMap . Vous obtiendrez une liste de paires Map.Entry <K, V> , qui sont toujours itérées dans le même ordre. Cet ordre est le même que l'ordre dans lequel vous placez les éléments. Vous pouvez également utiliser java.util.SortedMap , où les clés doivent avoir un ordre naturel ou le spécifier par un Comparator.

John Feminella
la source
14
Et pour éviter que le lecteur ne revérifie cela, car il est difficile de le vérifier en testant, la keySet()méthode renvoie efficacement un LinkedHashSet qui reflète l'ordre de vos put()appels. Notez que les appels répétés à put()la même clé ne changeront pas l'ordre sauf si vous avez remove()préalablement la clé.
Glenn Lawrence
De java 8 doc . LinkedHashMapdont l'ordre d'itération est l' ordre dans lequel ses entrées ont été consultées pour la dernière fois
TRiNE
24

LinkedHashMap maintient l'ordre des clés.

java.util.LinkedHashMap semble fonctionner comme un HashMap normal dans le cas contraire.

VoNWooDSoN
la source
1
Cela ne fournit pas de réponse à la question. Pour critiquer ou demander des éclaircissements à un auteur, laissez un commentaire sous son article - vous pouvez toujours commenter vos propres articles, et une fois que vous aurez une réputation suffisante, vous pourrez commenter n'importe quel article .
ianaya89
2
@ ianaya89 Je pense que c'est une vraie réponse, mais c'est très similaire à la réponse de John Feminella !
T30
1
Si vous souhaitez obtenir une carte ordonnée, où les entrées sont stockées dans cet ordre au fur et à mesure que vous les placez dans la carte, alors un LinkedHashMap est la bonne réponse. Si vous souhaitez trier les entrées dans votre carte indépendamment de l'ordre dans lequel vous les avez placées, alors SortedMap est la bonne réponse.
Ralph
@TRiNE Le document java 8 que vous avez lié indique qu'il existe deux modes de classement possibles: ordre d'insertion et ordre d'accès. Vous pensez à ce dernier qui est invoqué par l'utilisation d'un constructeur spécial public LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder). Le constructeur par défaut crée une instance LinkedHashMap ordonnée par insertion.
Artur Łysik
1
@ ArturŁysik oui c'est le cas. C'était une erreur que j'ai faite il y a quelques jours. Je vais le corriger. J'efface le commentaire. comme je ne peux plus le modifier
TRiNE
7

Je pense que la collection la plus proche que vous obtiendrez du cadre est le SortedMap

bruno conde
la source
3
Je voterais contre cette réponse si je pensais que cela valait la peine de perdre des points. Comme le souligne la réponse ci-dessus, votre réponse n'a pas les informations appropriées sur LinkedHashMap, et une petite explication de SortedMap serait bien aussi.
CorayThan
@CorayThan, dans ce cas, vous votez pour les meilleures réponses, pas pour les autres qui peuvent être correctes mais pas les meilleures ...
bruno conde
1
C'est ce que j'ai fait. Je dis simplement que je peux comprendre pourquoi quelqu'un voterait contre.
CorayThan
5

Vous pouvez tirer parti de l' interface NavigableMap qui peut être consultée et parcourue dans l'ordre croissant ou décroissant des clés. Cette interface est destinée à remplacer l'interface SortedMap. La carte navigable est généralement triée en fonction de l'ordre naturel de ses clés, ou par un comparateur fourni au moment de la création de la carte.

Il existe trois implémentations les plus utiles: TreeMap , ImmutableSortedMap et ConcurrentSkipListMap .

Exemple TreeMap:

TreeMap<String, Integer> users = new TreeMap<String, Integer>();
users.put("Bob", 1);
users.put("Alice", 2);
users.put("John", 3);

for (String key: users.keySet()) {
  System.out.println(key + " (ID = "+ users.get(key) + ")");
}

Production:

Alice (ID = 2)
Bob (ID = 1)
John (ID = 3)
Vitalii Fedorenko
la source
2

tl; dr

Pour conserver Map< Integer , String >un ordre trié par clé, utilisez l'une des deux classes implémentant les interfaces SortedMap/ NavigableMap:

  • TreeMap
  • ConcurrentSkipListMap

Si vous manipulez la carte dans un seul thread, utilisez le premier TreeMap,. Si vous manipulez sur des threads, utilisez le second ConcurrentSkipListMap,.

Pour plus de détails, consultez le tableau ci-dessous et la discussion suivante.

Détails

Voici un tableau graphique que j'ai fait montrant les fonctionnalités des dix Mapimplémentations fournies avec Java 11.

L' NavigableMapinterface est ce qui SortedMapaurait dû être en premier lieu. La SortedMaplogique devrait être supprimée mais ne peut pas l'être car certaines implémentations de cartes tierces peuvent utiliser l'interface.

Comme vous pouvez le voir dans ce tableau, seules deux classes implémentent les interfaces SortedMap/ NavigableMap:

Ces deux conservent les clés dans l'ordre trié, soit par leur ordre naturel (en utilisant la compareTométhode du Comparable( https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ Comparable.html ) ou par une Comparatorimplémentation que vous passez. La différence entre ces deux classes est que la seconde ConcurrentSkipListMap, est thread-safe , hautement concurrente .

Voir également la colonne Ordre d'itération dans le tableau ci-dessous.

  • La LinkedHashMapclasse renvoie ses entrées par l'ordre dans lequel elles ont été insérées à l'origine .
  • EnumMaprenvoie les entrées dans l'ordre dans lequel la classe d'énumération de la clé est définie . Par exemple, une carte indiquant quel employé couvre quel jour de la semaine ( Map< DayOfWeek , Person >) utilise la DayOfWeekclasse enum intégrée à Java. Cette énumération est définie avec lundi premier et dimanche dernier. Les entrées d'un itérateur apparaîtront donc dans cet ordre.

Les six autres implémentations ne font aucune promesse quant à l'ordre dans lequel elles signalent leurs entrées.

Tableau des implémentations de cartes dans Java 11, comparant leurs fonctionnalités

Basil Bourque
la source
0

J'ai utilisé la carte Simple Hash, la liste chaînée et les collections pour trier une carte par valeurs.

import java.util.*;
import java.util.Map.*;
public class Solution {

    public static void main(String[] args) {
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("JavaScript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
        // sort the linked list using Collections.sort()
        Collections.sort(list, new Comparator<Entry<String, Integer>>(){
        @Override
         public int compare(Entry<String, Integer> m1, Entry<String, Integer> m2) {
        return m1.getValue().compareTo(m2.getValue());
        }
      });
      for(Entry<String, Integer> value: list) {
         System.out.println(value);
     }
   }
}

La sortie est:

C=0
C++=1
Java=2
Python=3
JavaScript=4
Golang=5
Steffi Keran Rani J
la source