Classe Java qui implémente Map et conserve l'ordre d'insertion?

463

Je recherche une classe en java qui a une association clé-valeur, mais sans utiliser de hachage. Voici ce que je fais actuellement:

  1. Ajoutez des valeurs à a Hashtable.
  2. Obtenez un itérateur pour le Hashtable.entrySet().
  3. Parcourez toutes les valeurs et:
    1. Obtenez un Map.Entrypour l'itérateur.
    2. Créez un objet de type Module(une classe personnalisée) en fonction de la valeur.
    3. Ajoutez la classe à un JPanel.
  4. Montrez le panneau.

Le problème avec cela est que je n'ai pas de contrôle sur l'ordre dans lequel je récupère les valeurs, donc je ne peux pas afficher les valeurs dans l'ordre donné (sans coder en dur l'ordre).

J'utiliserais un ArrayListou Vectorpour cela, mais plus tard dans le code, je dois saisir l' Moduleobjet pour une clé donnée, ce que je ne peux pas faire avec un ArrayListou Vector.

Est-ce que quelqu'un connaît une classe Java libre / open-source qui fera cela, ou un moyen d'extraire des valeurs d'un en Hashtablefonction de leur date d'ajout?

Merci!

Shane
la source
1
Vous n'avez pas besoin d'utiliser entryset / map.entry. vous pouvez parcourir les clés et les valeurs en utilisant hashtable.keys comme énumération ou en utilisant hashtable.keyset.iterator.
John Gardner
5
J'ai pris la liberté de changer le titre, car ne pas utiliser de hachage n'est pas vraiment le problème, mais garder l'ordre d'insertion.
Joachim Sauer
Question similaire, Java Ordered Map
Basil Bourque

Réponses:

734

Je suggère un LinkedHashMapou un TreeMap. A LinkedHashMapconserve les clés dans l'ordre dans lequel elles ont été insérées, tandis que a TreeMapest conservé trié via a Comparatorou l' Comparableordre naturel des éléments.

Puisqu'il n'a pas à garder les éléments triés, LinkedHashMapdevrait être plus rapide dans la plupart des cas; TreeMapa O(log n)performance pour containsKey, get, put, et remove, selon le Javadocs, tandis que LinkedHashMapest O(1)pour chacun.

Si votre API qui n'attend qu'un ordre de tri prévisible, par opposition à un ordre de tri spécifique, envisagez d'utiliser les interfaces implémentées par ces deux classes, NavigableMapou SortedMap. Cela vous permettra de ne pas divulguer d'implémentations spécifiques dans votre API et de passer à l'une de ces classes spécifiques ou à une implémentation complètement différente à volonté par la suite.

Michael Myers
la source
2
Cela ne fonctionnera pas pour moi car, selon javadocs, cela ne donne que des valeurs ordonnées (via l'appel values ​​()). Existe-t-il un moyen d'obtenir des instances Map.Entry ordonnées?
Cory Kendall
1
@CoryKendall: TreeMap ne fonctionne-t-il pas? Il est censé être trié par clés et non par valeurs.
Michael Myers
1
Mon erreur, je pensais que les sets n'étaient pas triés.
Cory Kendall
61
Remarque: le tri d'un TreeMap est basé sur l'ordre naturel des clés: "La carte est triée selon l'ordre naturel de ses clés". Le LinkedHashMap est trié par ordre d'insertion. Grande différence!
Jop van Raaij
3
@AlexR: Cela n'est vrai que si le LinkedHashMap a été créé à l'aide du constructeur spécial fourni à cet effet. Par défaut, l'itération est dans l'ordre d'insertion.
Michael Myers
22

LinkedHashMap renverra les éléments dans l'ordre où ils ont été insérés dans la carte lorsque vous parcourez le keySet (), entrySet () ou les valeurs () de la carte.

Map<String, String> map = new LinkedHashMap<String, String>();

map.put("id", "1");
map.put("name", "rohan");
map.put("age", "26");

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

Cela imprimera les éléments dans l'ordre où ils ont été placés dans la carte:

id = 1
name = rohan 
age = 26 
Praveen Kishor
la source
16

Si une carte immuable correspond à vos besoins, il existe une bibliothèque de Google appelée goyave (voir aussi les questions sur la goyave )

Guava fournit un ImmutableMap avec un ordre d'itération fiable spécifié par l'utilisateur. Cette ImmutableMap a les performances O (1) pour containsKey, get. De toute évidence, les mises et suppressions ne sont pas prises en charge.

Les objets ImmutableMap sont construits à l'aide des élégantes méthodes de commodité statique de () et copyOf () ou d'un objet Builder .

jvdneste
la source
6

Vous pouvez conserver un Map(pour une recherche rapide) et List(pour une commande) mais un LinkedHashMappeut être le plus simple. Vous pouvez également essayer un SortedMapexemple TreeMap, qui a n'importe quelle commande que vous spécifiez.

Peter Lawrey
la source
1

Je ne sais pas si c'est open source, mais après un peu de recherche, j'ai trouvé cette implémentation de Map en utilisant ArrayList . Il semble qu'il s'agisse de Java antérieur à la version 1.5, vous pouvez donc le généraliser, ce qui devrait être facile. Notez que cette implémentation a un accès O (N), mais cela ne devrait pas être un problème si vous n'ajoutez pas des centaines de widgets à votre JPanel, ce que vous ne devriez pas de toute façon.

jpalecek
la source
1

Vous pouvez essayer mon implémentation Linked Tree Map .

Lawrence Dol
la source
1

Chaque fois que j'ai besoin de maintenir l'ordre naturel des choses connues à l'avance, j'utilise un EnumMap

les clés seront des énumérations et vous pourrez les insérer dans n'importe quel ordre, mais lorsque vous itérerez, elles seront répétées dans l'ordre énuméré (l'ordre naturel).

De plus, lorsque vous utilisez EnumMap, il ne devrait pas y avoir de collisions qui pourraient être plus efficaces.

Je trouve vraiment que l'utilisation de enumMap permet d'obtenir un code propre et lisible. Voici un exemple

j2emanue
la source
1

Vous pouvez utiliser LinkedHashMap pour l'ordre d'insertion principal dans la carte

Les points importants sur la classe Java LinkedHashMap sont:

  1. Il ne contient que des éléments uniques.
  2. Un LinkedHashMap contient des valeurs basées sur la clé 3. Il peut avoir une clé null et plusieurs valeurs null. 4.C'est la même chose que HashMap maintient à la place l'ordre d'insertion

    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> 

Mais si vous souhaitez trier les valeurs dans la carte à l'aide d'un objet défini par l'utilisateur ou d'une clé de type de données primitive, vous devez utiliser TreeMap Pour plus d'informations, reportez - vous à ce lien

Pankaj Goyal
la source
0

Vous pouvez soit utiliser, LinkedHashMap<K, V>soit implémenter votre propre CustomMap qui maintient l'ordre d'insertion.

Vous pouvez utiliser le suivant CustomHashMapavec les fonctionnalités suivantes:

  • L'ordre d'insertion est conservé, en utilisant LinkedHashMap en interne.
  • Les clés avec nullou des chaînes vides ne sont pas autorisées.
  • Une fois la clé avec valeur créée, nous ne remplaçons pas sa valeur.

HashMapvs LinkedHashMapvsCustomHashMap

interface CustomMap<K, V> extends Map<K, V> {
    public boolean insertionRule(K key, V value);
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public class CustomHashMap<K, V> implements CustomMap<K, V> {
    private Map<K, V> entryMap;
    // SET: Adds the specified element to this set if it is not already present.
    private Set<K> entrySet;

    public CustomHashMap() {
        super();
        entryMap = new LinkedHashMap<K, V>();
        entrySet = new HashSet();
    }

    @Override
    public boolean insertionRule(K key, V value) {
        // KEY as null and EMPTY String is not allowed.
        if (key == null || (key instanceof String && ((String) key).trim().equals("") ) ) {
            return false;
        }

        // If key already available then, we are not overriding its value.
        if (entrySet.contains(key)) { // Then override its value, but we are not allowing
            return false;
        } else { // Add the entry
            entrySet.add(key);
            entryMap.put(key, value);
            return true;
        }
    }
    public V put(K key, V value) {
        V oldValue = entryMap.get(key);
        insertionRule(key, value);
        return oldValue;
    }
    public void putAll(Map<? extends K, ? extends V> t) {
        for (Iterator i = t.keySet().iterator(); i.hasNext();) {
            K key = (K) i.next();
            insertionRule(key, t.get(key));
        }
    }

    public void clear() {
        entryMap.clear();
        entrySet.clear();
    }
    public boolean containsKey(Object key) {
        return entryMap.containsKey(key);
    }
    public boolean containsValue(Object value) {
        return entryMap.containsValue(value);
    }
    public Set entrySet() {
        return entryMap.entrySet();
    }
    public boolean equals(Object o) {
        return entryMap.equals(o);
    }
    public V get(Object key) {
        return entryMap.get(key);
    }
    public int hashCode() {
        return entryMap.hashCode();
    }
    public boolean isEmpty() {
        return entryMap.isEmpty();
    }
    public Set keySet() {
        return entrySet;
    }
    public V remove(Object key) {
        entrySet.remove(key);
        return entryMap.remove(key);
    }
    public int size() {
        return entryMap.size();
    }
    public Collection values() {
        return entryMap.values();
    }
}

Utilisation de CustomHashMap:

public static void main(String[] args) {
    System.out.println("== LinkedHashMap ==");
    Map<Object, String> map2 = new LinkedHashMap<Object, String>();
    addData(map2);

    System.out.println("== CustomHashMap ==");
    Map<Object, String> map = new CustomHashMap<Object, String>();
    addData(map);
}
public static void addData(Map<Object, String> map) {
    map.put(null, "1");
    map.put("name", "Yash");
    map.put("1", "1 - Str");
    map.put("1", "2 - Str"); // Overriding value
    map.put("", "1"); // Empty String
    map.put(" ", "1"); // Empty String
    map.put(1, "Int");
    map.put(null, "2"); // Null

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

O / P:

== LinkedHashMap == | == CustomHashMap ==
null = 2            | name = Yash
name = Yash         | 1 = 1 - Str
1 = 2 - Str         | 1 = Int
 = 1                |
  = 1               |
1 = Int             |

Si vous savez que les CLÉS sont fixes, vous pouvez utiliser EnumMap. Récupérer les valeurs du formulaire Propriétés / Fichiers XML

EX:

enum ORACLE {
    IP, URL, USER_NAME, PASSWORD, DB_Name;
}

EnumMap<ORACLE, String> props = new EnumMap<ORACLE, String>(ORACLE.class);
props.put(ORACLE.IP, "127.0.0.1");
props.put(ORACLE.URL, "...");
props.put(ORACLE.USER_NAME, "Scott");
props.put(ORACLE.PASSWORD, "Tiget");
props.put(ORACLE.DB_Name, "MyDB");
Yash
la source